diff --git a/libgraphtheory/autotests/test_kernel.cpp b/libgraphtheory/autotests/test_kernel.cpp index 2bb8a323..dffb7866 100644 --- a/libgraphtheory/autotests/test_kernel.cpp +++ b/libgraphtheory/autotests/test_kernel.cpp @@ -1,693 +1,693 @@ /* * Copyright 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 "test_kernel.h" #include "libgraphtheory/kernel/kernel.h" #include "libgraphtheory/graphdocument.h" #include "libgraphtheory/nodetype.h" #include "libgraphtheory/edgetype.h" #include "libgraphtheory/node.h" #include "libgraphtheory/edge.h" #include void TestKernel::initTestCase() { QVERIFY(GraphDocument::objects() == 0); QVERIFY(Node::objects() == 0); QVERIFY(Edge::objects() == 0); } void TestKernel::cleanupTestCase() { QVERIFY(GraphDocument::objects() == 0); QVERIFY(Node::objects() == 0); QVERIFY(Edge::objects() == 0); } void TestKernel::engineSetup() { GraphDocumentPtr document; NodePtr nodeA, nodeB; EdgePtr edge; QVERIFY(GraphDocument::objects() == 0); QVERIFY(Node::objects() == 0); QVERIFY(Edge::objects() == 0); // test destroy graph document document = GraphDocument::create(); nodeA = Node::create(document); nodeB = Node::create(document); edge = Edge::create(nodeA, nodeB); // create kernel QString script = "return true;"; Kernel kernel; QScriptValue result = kernel.execute(document, script); QCOMPARE(result.toBool(), true); document->destroy(); document.reset(); nodeA.reset(); nodeB.reset(); edge.reset(); QCOMPARE(Edge::objects(), uint(0)); QCOMPARE(Node::objects(), uint(0)); QCOMPARE(GraphDocument::objects(), uint(0)); } void TestKernel::graphObjectAccess() { GraphDocumentPtr document = GraphDocument::create(); NodePtr nodeA = Node::create(document); NodePtr nodeB = Node::create(document); EdgePtr edge = Edge::create(nodeA, nodeB); // test nodes Kernel kernel; QString script; QScriptValue result; script = "Document.nodes().length;"; result = kernel.execute(document, script); QCOMPARE(result.toInteger(), qreal(2)); script = "Document.edges().length;"; result = kernel.execute(document, script); QCOMPARE(result.toInteger(), qreal(1)); // cleanup document->destroy(); } void TestKernel::graphObjectAccessWithTypes() { GraphDocumentPtr document = GraphDocument::create(); NodeTypePtr nodeTypeB = NodeType::create(document); EdgeTypePtr edgeTypeB = EdgeType::create(document); NodePtr nodeA = Node::create(document); NodePtr nodeB = Node::create(document); NodePtr nodeC = Node::create(document); EdgePtr edgeAB = Edge::create(nodeA, nodeB); EdgePtr edgeBC = Edge::create(nodeB, nodeC); // setup types nodeA->setType(nodeTypeB); edgeAB->setType(edgeTypeB); // test nodes Kernel kernel; QString script; QScriptValue result; script = QString("Document.nodes(%1).length;").arg(nodeTypeB->id()); result = kernel.execute(document, script); QCOMPARE(result.toInteger(), qreal(1)); script = QString("Document.edges(%1).length;").arg(edgeTypeB->id()); result = kernel.execute(document, script); QCOMPARE(result.toInteger(), qreal(1)); // cleanup document->destroy(); } void TestKernel::nodeAccessMethods() { GraphDocumentPtr document = GraphDocument::create(); NodePtr node = Node::create(document); node->setId(42); // test nodes Kernel kernel; QString script; QScriptValue result; script = "Document.node(42).id;"; result = kernel.execute(document, script); QCOMPARE(result.toInteger(), qreal(42)); // cleanup document->destroy(); } void TestKernel::edgeAccessMethods() { GraphDocumentPtr document = GraphDocument::create(); document->edgeTypes().first()->setDirection(EdgeType::Unidirectional); NodePtr nodeA = Node::create(document); NodePtr nodeB = Node::create(document); EdgePtr edge = Edge::create(nodeA, nodeB); // test nodes Kernel kernel; QString script; QScriptValue result; script = "Document.nodes()[0].edges().length;"; result = kernel.execute(document, script); QCOMPARE(result.toInteger(), qreal(1)); script = "Document.nodes()[0].inEdges().length;"; result = kernel.execute(document, script); QCOMPARE(result.toInteger(), qreal(0)); script = "Document.nodes()[0].outEdges().length;"; result = kernel.execute(document, script); QCOMPARE(result.toInteger(), qreal(1)); // cleanup document->destroy(); } void TestKernel::edgeAccessMethodsWithTypes() { GraphDocumentPtr document = GraphDocument::create(); document->edgeTypes().first()->setDirection(EdgeType::Bidirectional); NodePtr nodeA = Node::create(document); NodePtr nodeB = Node::create(document); NodePtr nodeC = Node::create(document); EdgePtr edgeAB = Edge::create(nodeA, nodeB); EdgePtr edgeBC = Edge::create(nodeB, nodeC); // test edge type EdgeTypePtr edgeTypeB = EdgeType::create(document); edgeTypeB->setDirection(EdgeType::Unidirectional); edgeBC->setType(edgeTypeB); // test nodes Kernel kernel; QString script; QScriptValue result; script = QString("Document.nodes()[1].edges(%1).length;").arg(edgeTypeB->id()); result = kernel.execute(document, script); QCOMPARE(result.toInteger(), qreal(1)); script = QString("Document.nodes()[1].inEdges(%1).length;").arg(edgeTypeB->id()); result = kernel.execute(document, script); QCOMPARE(result.toInteger(), qreal(0)); script = QString("Document.nodes()[1].outEdges(%1).length;").arg(edgeTypeB->id()); result = kernel.execute(document, script); QCOMPARE(result.toInteger(), qreal(1)); // cleanup document->destroy(); } void TestKernel::nodeProperties() { GraphDocumentPtr document = GraphDocument::create(); document->edgeTypes().first()->setDirection(EdgeType::Unidirectional); NodePtr node = Node::create(document); node->setId(1); node->setX(20); node->setY(30); node->setColor("#ff0000"); // test nodes Kernel kernel; QString script; QScriptValue result; script = "Document.nodes()[0].id;"; result = kernel.execute(document, script); QCOMPARE(result.toString().toInt(), node->id()); script = "Document.nodes()[0].x;"; result = kernel.execute(document, script); QCOMPARE(result.toString().toDouble(), qreal(node->x())); script = "Document.nodes()[0].y;"; result = kernel.execute(document, script); QCOMPARE(result.toString().toDouble(), qreal(node->y())); script = "Document.nodes()[0].color;"; result = kernel.execute(document, script); QCOMPARE(result.toString(), QString("#ff0000")); // cleanup document->destroy(); } void TestKernel::nodeDynamicProperties() { GraphDocumentPtr document = GraphDocument::create(); NodeTypePtr type = document->nodeTypes().first(); NodePtr node = Node::create(document); type->addDynamicProperty("propertyA"); type->addDynamicProperty("propertyB"); type->addDynamicProperty("propertyC"); // test nodes Kernel kernel; QString script; QScriptValue result; // property read-access from script node->setDynamicProperty("propertyA", "1"); script = "Document.nodes()[0].propertyA;"; result = kernel.execute(document, script); QCOMPARE(result.toString().toInt(), 1); // property local write/read-access in script script = "Document.nodes()[0].propertyB = 2; Document.nodes()[0].propertyB"; kernel.execute(document, script); QCOMPARE(node->dynamicProperty("propertyB").toInt(), 2); // property write-access from script script = "Document.nodes()[0].propertyC = 3"; kernel.execute(document, script); QCOMPARE(node->dynamicProperty("propertyC").toInt(), 3); // cleanup document->destroy(); } void TestKernel::edgeProperties() { GraphDocumentPtr document = GraphDocument::create(); document->edgeTypes().first()->setDirection(EdgeType::Unidirectional); NodePtr nodeA = Node::create(document); NodePtr nodeB = Node::create(document); EdgePtr edge = Edge::create(nodeA, nodeB); // test nodes Kernel kernel; QString script; QScriptValue result; script = "Document.nodes()[0].edges()[0].from().id;"; result = kernel.execute(document, script); QCOMPARE(result.toString().toInt(), nodeA->id()); script = "Document.nodes()[0].edges()[0].to().id;"; result = kernel.execute(document, script); QCOMPARE(result.toString().toInt(), nodeB->id()); script = "Document.nodes()[0].edges()[0].directed();"; result = kernel.execute(document, script); QCOMPARE(result.toBool(), true); // cleanup document->destroy(); } void TestKernel::edgeDynamicProperties() { GraphDocumentPtr document = GraphDocument::create(); EdgeTypePtr type = document->edgeTypes().first(); NodePtr nodeA = Node::create(document); NodePtr nodeB = Node::create(document); EdgePtr edge = Edge::create(nodeA, nodeB); type->addDynamicProperty("propertyA"); type->addDynamicProperty("propertyB"); type->addDynamicProperty("propertyC"); // test nodes Kernel kernel; QString script; QScriptValue result; // property read-access from script edge->setDynamicProperty("propertyA", "1"); script = "Document.edges()[0].propertyA;"; result = kernel.execute(document, script); QCOMPARE(result.toString().toInt(), 1); // property local write/read-access in script script = "Document.edges()[0].propertyB = 2; Document.edges()[0].propertyB"; kernel.execute(document, script); QCOMPARE(edge->dynamicProperty("propertyB").toInt(), 2); // property write-access from script script = "Document.edges()[0].propertyC = 3"; kernel.execute(document, script); QCOMPARE(edge->dynamicProperty("propertyC").toInt(), 3); // cleanup document->destroy(); } void TestKernel::nodeTypes() { GraphDocumentPtr document = GraphDocument::create(); document->nodeTypes().first()->setId(1); NodeTypePtr typeB = NodeType::create(document); typeB->setId(2); NodePtr node = Node::create(document); // test nodes Kernel kernel; QString script; QScriptValue result; script = "Document.nodes()[0].type;"; result = kernel.execute(document, script); QCOMPARE(result.toString().toInt(), 1); script = "Document.nodes()[0].type = 2;"; kernel.execute(document, script); QCOMPARE(node->type()->id(), 2); script = "Document.nodes()[0].type;"; result = kernel.execute(document, script); QCOMPARE(result.toString().toInt(), 2); // cleanup document->destroy(); } void TestKernel::edgeTypes() { GraphDocumentPtr document = GraphDocument::create(); document->edgeTypes().first()->setId(1); EdgeTypePtr typeB = EdgeType::create(document); typeB->setId(2); NodePtr nodeA = Node::create(document); NodePtr nodeB = Node::create(document); EdgePtr edge = Edge::create(nodeA, nodeB); // test nodes Kernel kernel; QString script; QScriptValue result; script = "Document.edges()[0].type;"; result = kernel.execute(document, script); QCOMPARE(result.toString().toInt(), 1); script = "Document.edges()[0].type = 2;"; kernel.execute(document, script); QCOMPARE(edge->type()->id(), 2); script = "Document.edges()[0].type;"; result = kernel.execute(document, script); QCOMPARE(result.toString().toInt(), 2); // cleanup document->destroy(); } void TestKernel::neighborships() { GraphDocumentPtr document = GraphDocument::create(); document->edgeTypes().first()->setDirection(EdgeType::Unidirectional); NodePtr nodeA = Node::create(document); NodePtr nodeB = Node::create(document); EdgePtr edge = Edge::create(nodeA, nodeB); // test nodes Kernel kernel; QString script; QScriptValue result; // test with unidirectional edge script = "Document.nodes()[0].neighbors().length;"; result = kernel.execute(document, script); QCOMPARE(result.toInteger(), qreal(1)); script = "Document.nodes()[1].neighbors().length;"; result = kernel.execute(document, script); QCOMPARE(result.toInteger(), qreal(1)); script = "Document.nodes()[0].successors().length;"; result = kernel.execute(document, script); QCOMPARE(result.toInteger(), qreal(1)); script = "Document.nodes()[1].successors().length;"; result = kernel.execute(document, script); QCOMPARE(result.toInteger(), qreal(0)); script = "Document.nodes()[0].predecessors().length;"; result = kernel.execute(document, script); QCOMPARE(result.toInteger(), qreal(0)); script = "Document.nodes()[1].predecessors().length;"; result = kernel.execute(document, script); QCOMPARE(result.toInteger(), qreal(1)); // test with bidirectional edge document->edgeTypes().first()->setDirection(EdgeType::Bidirectional); script = "Document.nodes()[0].neighbors().length;"; result = kernel.execute(document, script); QCOMPARE(result.toInteger(), qreal(1)); script = "Document.nodes()[1].neighbors().length;"; result = kernel.execute(document, script); QCOMPARE(result.toInteger(), qreal(1)); script = "Document.nodes()[0].successors().length;"; result = kernel.execute(document, script); QCOMPARE(result.toInteger(), qreal(1)); script = "Document.nodes()[1].successors().length;"; result = kernel.execute(document, script); QCOMPARE(result.toInteger(), qreal(1)); script = "Document.nodes()[0].predecessors().length;"; result = kernel.execute(document, script); QCOMPARE(result.toInteger(), qreal(1)); script = "Document.nodes()[1].predecessors().length;"; result = kernel.execute(document, script); QCOMPARE(result.toInteger(), qreal(1)); // cleanup document->destroy(); } void TestKernel::neighborshipsWithTypes() { - // this test is the same as withouth types but we add an backedge of another type that + // this test is the same as without types but we add an backedge of another type that // changes the results if types are not respected GraphDocumentPtr document = GraphDocument::create(); EdgeTypePtr typeA = document->edgeTypes().first(); EdgeTypePtr typeB = EdgeType::create(document); typeA->setDirection(EdgeType::Unidirectional); typeA->setId(1); typeB->setDirection(EdgeType::Unidirectional); typeB->setId(2); NodePtr nodeA = Node::create(document); NodePtr nodeB = Node::create(document); EdgePtr edgeAB = Edge::create(nodeA, nodeB); EdgePtr edgeBA = Edge::create(nodeB, nodeA); edgeBA->setType(typeB); // test nodes Kernel kernel; QString script; QScriptValue result; // test with unidirectional edge script = "Document.nodes()[0].neighbors(1).length;"; result = kernel.execute(document, script); QCOMPARE(result.toInteger(), qreal(1)); script = "Document.nodes()[1].neighbors(1).length;"; result = kernel.execute(document, script); QCOMPARE(result.toInteger(), qreal(1)); script = "Document.nodes()[0].successors(1).length;"; result = kernel.execute(document, script); QCOMPARE(result.toInteger(), qreal(1)); script = "Document.nodes()[1].successors(1).length;"; result = kernel.execute(document, script); QCOMPARE(result.toInteger(), qreal(0)); script = "Document.nodes()[0].predecessors(1).length;"; result = kernel.execute(document, script); QCOMPARE(result.toInteger(), qreal(0)); script = "Document.nodes()[1].predecessors(1).length;"; result = kernel.execute(document, script); QCOMPARE(result.toInteger(), qreal(1)); // test with bidirectional edge document->edgeTypes().first()->setDirection(EdgeType::Bidirectional); script = "Document.nodes()[0].neighbors(1).length;"; result = kernel.execute(document, script); QCOMPARE(result.toInteger(), qreal(1)); script = "Document.nodes()[1].neighbors(1).length;"; result = kernel.execute(document, script); QCOMPARE(result.toInteger(), qreal(1)); script = "Document.nodes()[0].successors(1).length;"; result = kernel.execute(document, script); QCOMPARE(result.toInteger(), qreal(1)); script = "Document.nodes()[1].successors(1).length;"; result = kernel.execute(document, script); QCOMPARE(result.toInteger(), qreal(1)); script = "Document.nodes()[0].predecessors(1).length;"; result = kernel.execute(document, script); QCOMPARE(result.toInteger(), qreal(1)); script = "Document.nodes()[1].predecessors(1).length;"; result = kernel.execute(document, script); QCOMPARE(result.toInteger(), qreal(1)); // cleanup document->destroy(); } void TestKernel::automaticScriptObjectPropertyGeneration() { GraphDocumentPtr document = GraphDocument::create(); NodePtr nodeA = Node::create(document); NodePtr nodeB = Node::create(document); EdgePtr edge = Edge::create(nodeA, nodeB); Kernel kernel; QString script; QScriptValue result; // For edges/nodes we can assign arbitrary dynamic properties during // script execution. However, they exist only during execution and are removed // at the end of the execution. script = "Document.nodes()[0].nonRegProp=1; Document.nodes()[0].nonRegProp;"; result = kernel.execute(document, script); QCOMPARE(result.toString().toInt(), 1); QCOMPARE(nodeA->dynamicProperty("nonRegProp").toInt(), 0); script = "Document.edges()[0].nonRegProp=1; Document.edges()[0].nonRegProp;"; result = kernel.execute(document, script); QCOMPARE(result.toString().toInt(), 1); QCOMPARE(edge->dynamicProperty("nonRegProp").toInt(), 0); // cleanup document->destroy(); } void TestKernel::createNode() { GraphDocumentPtr document = GraphDocument::create(); // test nodes Kernel kernel; QString script; QScriptValue result; script = "Document.createNode(0, 0);"; result = kernel.execute(document, script); QCOMPARE(document->nodes().count(), 1); // cleanup document->destroy(); } void TestKernel::createEdge() { GraphDocumentPtr document = GraphDocument::create(); NodePtr nodeA = Node::create(document); NodePtr nodeB = Node::create(document); // test nodes Kernel kernel; QString script; QScriptValue result; script = "Document.createEdge(Document.nodes()[0], Document.nodes()[1]);"; result = kernel.execute(document, script); QCOMPARE(document->edges().count(), 1); // cleanup document->destroy(); } void TestKernel::deleteNode() { GraphDocumentPtr document = GraphDocument::create(); Node::create(document); // test nodes Kernel kernel; QString script; QScriptValue result; QCOMPARE(document->nodes().count(), 1); script = "Document.remove(Document.nodes()[0]);"; result = kernel.execute(document, script); QCOMPARE(document->nodes().count(), 0); // cleanup document->destroy(); } void TestKernel::deleteEdge() { GraphDocumentPtr document = GraphDocument::create(); NodePtr nodeA = Node::create(document); NodePtr nodeB = Node::create(document); Edge::create(nodeA, nodeB); // test nodes Kernel kernel; QString script; QScriptValue result; QCOMPARE(document->edges().count(), 1); script = "Document.remove(Document.edges()[0]);"; result = kernel.execute(document, script); QCOMPARE(document->edges().count(), 0); // cleanup document->destroy(); } void TestKernel::distance() { GraphDocumentPtr document = GraphDocument::create(); NodePtr nodeA = Node::create(document); NodePtr nodeB = Node::create(document); NodePtr nodeC = Node::create(document); EdgePtr edgeAB = Edge::create(nodeA, nodeB); EdgePtr edgeBC = Edge::create(nodeB, nodeC); document->edgeTypes().first()->addDynamicProperty("dist"); document->edgeTypes().first()->setDirection(EdgeType::Bidirectional); edgeAB->setDynamicProperty("dist", "1"); edgeBC->setDynamicProperty("dist", "1"); // test nodes Kernel kernel; QString script; QScriptValue result; script = "Document.nodes()[0].distance(\"dist\", Document.nodes())[2];"; result = kernel.execute(document, script); QCOMPARE(result.toInteger(), qreal(2)); script = "Document.nodes()[2].distance(\"dist\", Document.nodes())[0];"; result = kernel.execute(document, script); QCOMPARE(result.toInteger(), qreal(2)); // cleanup document->destroy(); } QTEST_MAIN(TestKernel) diff --git a/libgraphtheory/editorplugins/generategraph/generategraphwidget.h b/libgraphtheory/editorplugins/generategraph/generategraphwidget.h index bd3076cf..1eafb742 100644 --- a/libgraphtheory/editorplugins/generategraph/generategraphwidget.h +++ b/libgraphtheory/editorplugins/generategraph/generategraphwidget.h @@ -1,156 +1,156 @@ /* This file is part of Rocs. Copyright (C) 2011-2013 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 . */ #ifndef GENERATEGRAPHWIDGET_H #define GENERATEGRAPHWIDGET_H #include "ui_generategraphwidget.h" #include "typenames.h" #include #include namespace GraphTheory { class GenerateGraphWidget : public QDialog { Q_OBJECT enum GraphGenerator { MeshGraph, StarGraph, CircleGraph, RandomEdgeGraph, ErdosRenyiRandomGraph, RandomTree }; public: explicit GenerateGraphWidget(GraphDocumentPtr document, QWidget *parent = 0); ~GenerateGraphWidget(); public slots: /** * Select graph generator by its index. * * \param index the index of the graph generator */ void setGraphGenerator(int index); /** * Set seed for the internal random number generator. * * \param seed is the random number generator seed. */ void setSeed(int seed); /** * Set the type of node elements for the generator. * * \param type is the NodeType ID */ void setNodeType(int type); /** * Set the type of edges for the generator. * * \param type is the NodeType ID */ void setEdgeType(int type); /** * Set the unique graph identifier for the next generated graph. * If \p identifier is already in use, the lexicographically next identifier will be used. * * \param identifier for the next generated graph */ void setGraphIdentifier(const QString &identifier); /** * Generate the graph with the previously set configuration. */ void generateGraph(); private: QPointF documentCenter() const; /** * Generate circle graph with specified number of nodes. * * \param nodes is the number of nodes of the generated graph */ void generateCircle(int nodes); /** * Generate mesh graph with specified number of \p rows and \p columns. * * \param rows is the number of rows of the generated graph * \param columns is the number of columns of the generated graph */ void generateMesh(int rows, int columns); /** * Generate a random graph by assigning uniformly at random \p edges many edges to the set of nodes. * * \param nodes is the number of nodes of the generated graph * \param edges is the number of edges of the generated graph * \param seed is the seed for random number generator * \param selfEdges if true self edges are generated, otherwise not */ void generateRandomGraph(int nodes, int edges, bool selfEdges); /** * Generate a star graph with specified number of nodes. * * \param satelliteNodes is the number of satellite nodes of the generated graph */ void generateStar(int satelliteNodes); /** * Generate an Erdös-Renyi random graph. This graph is created by first creating a set of \p nodes - * many node elemnts and then creating any possibly edge with probability \p edgeProbability. + * many node elements and then creating any possibly edge with probability \p edgeProbability. * * \param nodes is the number of nodes of the generated graph * \param edgeProbability is the probability for creating an arbitrary edge * \param seed is the seed for random number generator * \param selfEdges if true self edges are generated, otherwise not */ void generateErdosRenyiRandomGraph(int nodes, double edgeProbability, bool selfEdges); /** * Generate a random tree by iterating the following process: First create a root node and than * for (\p nodes - 1) rounds create for a node (selected uniformly at random) a child. * * \param nodes is number of nodes * \param seed is the seed for random number generator */ void generateRandomTreeGraph(int nodes); GraphDocumentPtr m_document; int m_seed; NodeTypePtr m_nodeType; EdgeTypePtr m_edgeType; QString m_identifier; GraphGenerator m_graphGenerator; QHash m_defaultIdentifiers; Ui::GenerateGraphWidget *ui; }; } #endif diff --git a/libgraphtheory/fileformats/dot/autotests/testdotfileformat.cpp b/libgraphtheory/fileformats/dot/autotests/testdotfileformat.cpp index 163e9857..ab186dd7 100644 --- a/libgraphtheory/fileformats/dot/autotests/testdotfileformat.cpp +++ b/libgraphtheory/fileformats/dot/autotests/testdotfileformat.cpp @@ -1,725 +1,725 @@ /* This file is part of Rocs. 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 "testdotfileformat.h" #include "../dotfileformat.h" #include "../dotgrammar.h" #include "fileformats/fileformatinterface.h" #include "graphdocument.h" #include "node.h" #include "edge.h" #include "logging_p.h" #include #include #include using namespace GraphTheory; static const std::string simple = "digraph simple {a_2 -> b; c; d -> e /* error -> comment*/}"; static const std::string subgraph = "digraph trees {" " subgraph t {" " 0 -> \"1\" [label = \"A\"];" " 0 -> \"2\" [label = \"B\"];" " }" " subgraph u {" " Animal -> Cat [label = \"feline\"];" " Animal -> Dot [label = \"canine\"];" " }" "}"; void DotFileFormatTest::checkNodes(GraphDocumentPtr document, QList nodeNames) { NodeList nodes = document->nodes(); foreach(const NodePtr &node, nodes) { qCDebug(GRAPHTHEORY_FILEFORMAT) << "test NODE " << node->dynamicProperty("name").toString(); } foreach(const NodePtr &node, nodes) { QString name = node->dynamicProperty("name").toString(); int index = nodeNames.indexOf(name); if (index == -1) { qCDebug(GRAPHTHEORY_FILEFORMAT) << "Node "<< name << " was created unnecessarily."; } QVERIFY(index != -1); nodeNames.removeAt(index); } QVERIFY(nodeNames.isEmpty()); } void DotFileFormatTest::init() { } void DotFileFormatTest::simpleGraphParsing() { GraphDocumentPtr document = GraphDocument::create(); QVERIFY(DotParser::parse(simple, document)); QVERIFY(document->nodes().count() == 5); QVERIFY(document->edges().count() == 2); } void DotFileFormatTest::parseSubgraphs() { GraphDocumentPtr document = GraphDocument::create(); QVERIFY(DotParser::parse(subgraph, document)); } void DotFileFormatTest::parseFileER() { // create importer plugin DotFileFormat importer(this, QList()); importer.setFile(QUrl::fromLocalFile("undirected/ER.gv")); importer.readFile(); QVERIFY2(importer.hasError() == false, importer.errorString().toStdString().c_str()); GraphDocumentPtr document = importer.graphDocument(); document->nodeTypes().first()->addDynamicProperty("label"); // Check that all of the node names were imported, and that there are no extras. QList nodeNames; nodeNames << "course" << "institute" << "student" << "name0" << "name1" << "name2" << "code" << "grade" << "number" << "C-I" << "S-C" << "S-I"; checkNodes(document, nodeNames); // Check the numbers of edges QVERIFY(document->edges().count() == 12); // Check that a pointer has the correct label & that the node labels work. NodeList nodes = document->nodes(); NodePtr from, to; foreach(const NodePtr &node, nodes) { QString name = node->dynamicProperty("label").toString(); if (name == "student") { from = node; } if (name == "S-C") { to = node; } if (name == "name0" || name == "name1" || name == "name2") { QVERIFY(node->dynamicProperty("label").toString() == "name"); } } // EdgePtr connection = from->edges(to).at(0); //FIXME API missing // QVERIFY(connection->property("label") == "m"); } void DotFileFormatTest::parseFileHeawood() { // create importer plugin DotFileFormat importer(this, QList()); importer.setFile(QUrl::fromLocalFile("undirected/Heawood.gv")); importer.readFile(); QVERIFY2(importer.hasError() == false, importer.errorString().toStdString().c_str()); } void DotFileFormatTest::parseFileNgk104() { // create importer plugin DotFileFormat importer(this, QList()); importer.setFile(QUrl::fromLocalFile("undirected/ngk10_4.gv")); importer.readFile(); QVERIFY2(importer.hasError() == false, importer.errorString().toStdString().c_str()); } void DotFileFormatTest::parseFilePetersen() { // create importer plugin DotFileFormat importer(this, QList()); importer.setFile(QUrl::fromLocalFile("undirected/Petersen.gv")); importer.readFile(); QVERIFY2(importer.hasError() == false, importer.errorString().toStdString().c_str()); } void DotFileFormatTest::parseFileProcess() { // create importer plugin DotFileFormat importer(this, QList()); importer.setFile(QUrl::fromLocalFile("undirected/process.gv")); importer.readFile(); QVERIFY2(importer.hasError() == false, importer.errorString().toStdString().c_str()); GraphDocumentPtr document = importer.graphDocument(); // check that all of the node names were imported, and that there are no extras QStringList nodeNames; nodeNames << "run" << "intr" << "runbl" << "kernel" << "zombie" << "sleep" << "swap" << "runmem" << "runswap" << "new"; checkNodes(document, nodeNames); // Check the numbers of pointers QVERIFY(document->edges().count() == 13); } void DotFileFormatTest::parseFileAbstract() { // create importer plugin DotFileFormat importer(this, QList()); importer.setFile(QUrl::fromLocalFile("directed/abstract.gv")); importer.readFile(); QVERIFY2(importer.hasError() == false, importer.errorString().toStdString().c_str()); } void DotFileFormatTest::parseFileAlf() { // create importer plugin DotFileFormat importer(this, QList()); importer.setFile(QUrl::fromLocalFile("directed/alf.gv")); importer.readFile(); QVERIFY2(importer.hasError() == false, importer.errorString().toStdString().c_str()); } void DotFileFormatTest::parseFileArrows() { // create importer plugin DotFileFormat importer(this, QList()); importer.setFile(QUrl::fromLocalFile("directed/arrows.gv")); importer.readFile(); QEXPECT_FAIL("", "File contains invalid identifiers with underbar at beginning.", Continue); QVERIFY2(importer.hasError() == false, importer.errorString().toStdString().c_str()); } void DotFileFormatTest::parseFileAwilliams() { // create importer plugin DotFileFormat importer(this, QList()); importer.setFile(QUrl::fromLocalFile("directed/awilliams.gv")); importer.readFile(); QVERIFY2(importer.hasError() == false, importer.errorString().toStdString().c_str()); } void DotFileFormatTest::parseFileClust() { // create importer plugin DotFileFormat importer(this, QList()); importer.setFile(QUrl::fromLocalFile("directed/clust.gv")); importer.readFile(); QVERIFY2(importer.hasError() == false, importer.errorString().toStdString().c_str()); } void DotFileFormatTest::parseFileClust1() { // create importer plugin DotFileFormat importer(this, QList()); importer.setFile(QUrl::fromLocalFile("directed/clust1.gv")); importer.readFile(); QVERIFY2(importer.hasError() == false, importer.errorString().toStdString().c_str()); } void DotFileFormatTest::parseFileClust2() { // create importer plugin DotFileFormat importer(this, QList()); importer.setFile(QUrl::fromLocalFile("directed/clust2.gv")); importer.readFile(); QVERIFY2(importer.hasError() == false, importer.errorString().toStdString().c_str()); } void DotFileFormatTest::parseFileClust3() { // create importer plugin DotFileFormat importer(this, QList()); importer.setFile(QUrl::fromLocalFile("directed/clust3.gv")); importer.readFile(); QVERIFY2(importer.hasError() == false, importer.errorString().toStdString().c_str()); } void DotFileFormatTest::parseFileClust4() { // create importer plugin DotFileFormat importer(this, QList()); importer.setFile(QUrl::fromLocalFile("directed/clust4.gv")); importer.readFile(); QVERIFY2(importer.hasError() == false, importer.errorString().toStdString().c_str()); } void DotFileFormatTest::parseFileClust5() { // create importer plugin DotFileFormat importer(this, QList()); importer.setFile(QUrl::fromLocalFile("directed/clust5.gv")); importer.readFile(); QVERIFY2(importer.hasError() == false, importer.errorString().toStdString().c_str()); } void DotFileFormatTest::parseFileCrazy() { // create importer plugin DotFileFormat importer(this, QList()); importer.setFile(QUrl::fromLocalFile("directed/crazy.gv")); importer.readFile(); QVERIFY2(importer.hasError() == false, importer.errorString().toStdString().c_str()); } void DotFileFormatTest::parseFileCtext() { // create importer plugin DotFileFormat importer(this, QList()); importer.setFile(QUrl::fromLocalFile("directed/ctext.gv")); importer.readFile(); QVERIFY2(importer.hasError() == false, importer.errorString().toStdString().c_str()); } void DotFileFormatTest::parseFileDfa() { // create importer plugin DotFileFormat importer(this, QList()); importer.setFile(QUrl::fromLocalFile("directed/dfa.gv")); importer.readFile(); QVERIFY2(importer.hasError() == false, importer.errorString().toStdString().c_str()); } void DotFileFormatTest::parseFileFig6() { // create importer plugin DotFileFormat importer(this, QList()); importer.setFile(QUrl::fromLocalFile("directed/fig6.gv")); importer.readFile(); QVERIFY2(importer.hasError() == false, importer.errorString().toStdString().c_str()); } void DotFileFormatTest::parseFileFsm() { // create importer plugin DotFileFormat importer(this, QList()); importer.setFile(QUrl::fromLocalFile("directed/fsm.gv")); importer.readFile(); QVERIFY2(importer.hasError() == false, importer.errorString().toStdString().c_str()); GraphDocumentPtr document = importer.graphDocument(); // check that all of the node names were imported, and that there are no extras QStringList nodeNames; nodeNames << "LR_0" << "LR_1" << "LR_2" << "LR_3" << "LR_4" << "LR_5" << "LR_6" << "LR_7" << "LR_8"; checkNodes(document, nodeNames); // Check the numbers of pointers QVERIFY(document->edges().count() == 14); // Check that a pointer has the correct label and that the shapes are correct. NodeList nodes = document->nodes(); QVERIFY(nodes.length() == 9); // Make the test quit because the parser has too many LR nodes. NodePtr from, to; foreach(const NodePtr &node, nodes) { QString name = node->property("name").toString(); if (name == "LR_0") { from = node; QVERIFY(node->property("shape").toString() == "doublecircle"); } if (name == "LR_2") { to = node; QVERIFY(node->property("shape").toString() == "circle"); } } // EdgePtr connection = from->edges(to).at(0); //FIXME API missing // QVERIFY(connection->property("label") == "SS(B)"); } void DotFileFormatTest::parseFileKW91() { // create importer plugin DotFileFormat importer(this, QList()); importer.setFile(QUrl::fromLocalFile("directed/KW91.gv")); importer.readFile(); QVERIFY2(importer.hasError() == false, importer.errorString().toStdString().c_str()); } void DotFileFormatTest::parseFileLatin1() { // create importer plugin DotFileFormat importer(this, QList()); importer.setFile(QUrl::fromLocalFile("directed/Latin1.gv")); importer.readFile(); QVERIFY2(importer.hasError() == false, importer.errorString().toStdString().c_str()); } void DotFileFormatTest::parseFileNaN() { // create importer plugin DotFileFormat importer(this, QList()); importer.setFile(QUrl::fromLocalFile("directed/NaN.gv")); importer.readFile(); QVERIFY2(importer.hasError() == false, importer.errorString().toStdString().c_str()); } void DotFileFormatTest::parseFileGrammar() { // create importer plugin DotFileFormat importer(this, QList()); importer.setFile(QUrl::fromLocalFile("directed/grammar.gv")); importer.readFile(); QVERIFY2(importer.hasError() == false, importer.errorString().toStdString().c_str()); } void DotFileFormatTest::parseFileHashtable() { // create importer plugin DotFileFormat importer(this, QList()); importer.setFile(QUrl::fromLocalFile("directed/hashtable.gv")); importer.readFile(); QVERIFY2(importer.hasError() == false, importer.errorString().toStdString().c_str()); } void DotFileFormatTest::parseFileHondaTokoro() { // create importer plugin DotFileFormat importer(this, QList()); importer.setFile(QUrl::fromLocalFile("directed/honda-tokoro.gv")); importer.readFile(); QVERIFY2(importer.hasError() == false, importer.errorString().toStdString().c_str()); } void DotFileFormatTest::parseFileJapanese() { // create importer plugin DotFileFormat importer(this, QList()); importer.setFile(QUrl::fromLocalFile("directed/japanese.gv")); importer.readFile(); QVERIFY2(importer.hasError() == false, importer.errorString().toStdString().c_str()); } void DotFileFormatTest::parseFileJcctree() { // create importer plugin DotFileFormat importer(this, QList()); importer.setFile(QUrl::fromLocalFile("directed/jcctree.gv")); importer.readFile(); QVERIFY2(importer.hasError() == false, importer.errorString().toStdString().c_str()); } void DotFileFormatTest::parseFileJsort() { // create importer plugin DotFileFormat importer(this, QList()); importer.setFile(QUrl::fromLocalFile("directed/jsort.gv")); importer.readFile(); QVERIFY2(importer.hasError() == false, importer.errorString().toStdString().c_str()); } void DotFileFormatTest::parseFileLdbxtried() { // create importer plugin DotFileFormat importer(this, QList()); importer.setFile(QUrl::fromLocalFile("directed/ldbxtried.gv")); importer.readFile(); QVERIFY2(importer.hasError() == false, importer.errorString().toStdString().c_str()); } void DotFileFormatTest::parseFileLongflat() { // create importer plugin DotFileFormat importer(this, QList()); importer.setFile(QUrl::fromLocalFile("directed/longflat.gv")); importer.readFile(); QVERIFY2(importer.hasError() == false, importer.errorString().toStdString().c_str()); } void DotFileFormatTest::parseFileMike() { // create importer plugin DotFileFormat importer(this, QList()); importer.setFile(QUrl::fromLocalFile("directed/mike.gv")); importer.readFile(); QVERIFY2(importer.hasError() == false, importer.errorString().toStdString().c_str()); } void DotFileFormatTest::parseFileNhg() { // create importer plugin DotFileFormat importer(this, QList()); importer.setFile(QUrl::fromLocalFile("directed/nhg.gv")); importer.readFile(); QVERIFY2(importer.hasError() == false, importer.errorString().toStdString().c_str()); } void DotFileFormatTest::parseFileOldarrows() { // create importer plugin DotFileFormat importer(this, QList()); importer.setFile(QUrl::fromLocalFile("directed/oldarrows.gv")); importer.readFile(); QVERIFY2(importer.hasError() == false, importer.errorString().toStdString().c_str()); } void DotFileFormatTest::parseFilePgram() { // create importer plugin DotFileFormat importer(this, QList()); importer.setFile(QUrl::fromLocalFile("directed/pgram.gv")); importer.readFile(); QVERIFY2(importer.hasError() == false, importer.errorString().toStdString().c_str()); } void DotFileFormatTest::parseFilePm2way() { // create importer plugin DotFileFormat importer(this, QList()); importer.setFile(QUrl::fromLocalFile("directed/pm2way.gv")); importer.readFile(); QVERIFY2(importer.hasError() == false, importer.errorString().toStdString().c_str()); } void DotFileFormatTest::parseFilePmpipe() { // create importer plugin DotFileFormat importer(this, QList()); importer.setFile(QUrl::fromLocalFile("directed/pmpipe.gv")); importer.readFile(); QVERIFY2(importer.hasError() == false, importer.errorString().toStdString().c_str()); } void DotFileFormatTest::parseFilePolypoly() { // create importer plugin DotFileFormat importer(this, QList()); importer.setFile(QUrl::fromLocalFile("directed/polypoly.gv")); importer.readFile(); QEXPECT_FAIL("", "Not parsing with unknown reason: need to investigate further.", Continue); QVERIFY2(importer.hasError() == false, importer.errorString().toStdString().c_str()); } void DotFileFormatTest::parseFileProc3d() { // create importer plugin DotFileFormat importer(this, QList()); importer.setFile(QUrl::fromLocalFile("directed/proc3d.gv")); importer.readFile(); QVERIFY2(importer.hasError() == false, importer.errorString().toStdString().c_str()); } void DotFileFormatTest::parseFilePsfonttest() { // create importer plugin DotFileFormat importer(this, QList()); importer.setFile(QUrl::fromLocalFile("directed/psfonttest.gv")); importer.readFile(); QVERIFY2(importer.hasError() == false, importer.errorString().toStdString().c_str()); } void DotFileFormatTest::parseFileRecord2() { // create importer plugin DotFileFormat importer(this, QList()); importer.setFile(QUrl::fromLocalFile("directed/record2.gv")); importer.readFile(); QVERIFY2(importer.hasError() == false, importer.errorString().toStdString().c_str()); } void DotFileFormatTest::parseFileRecords() { // create importer plugin DotFileFormat importer(this, QList()); importer.setFile(QUrl::fromLocalFile("directed/records.gv")); importer.readFile(); QVERIFY2(importer.hasError() == false, importer.errorString().toStdString().c_str()); } void DotFileFormatTest::parseFileRowe() { // create importer plugin DotFileFormat importer(this, QList()); importer.setFile(QUrl::fromLocalFile("directed/rowe.gv")); importer.readFile(); QVERIFY2(importer.hasError() == false, importer.errorString().toStdString().c_str()); } void DotFileFormatTest::parseFileRussian() { // create importer plugin DotFileFormat importer(this, QList()); importer.setFile(QUrl::fromLocalFile("directed/russian.gv")); importer.readFile(); - QEXPECT_FAIL("", "Parsing of cyrillic characters for identifiers not yet supported.", Continue); + QEXPECT_FAIL("", "Parsing of Cyrillic characters for identifiers not yet supported.", Continue); QVERIFY2(importer.hasError() == false, importer.errorString().toStdString().c_str()); } void DotFileFormatTest::parseFileSdh() { // create importer plugin DotFileFormat importer(this, QList()); importer.setFile(QUrl::fromLocalFile("directed/sdh.gv")); importer.readFile(); QVERIFY2(importer.hasError() == false, importer.errorString().toStdString().c_str()); } void DotFileFormatTest::parseFileShells() { // create importer plugin DotFileFormat importer(this, QList()); importer.setFile(QUrl::fromLocalFile("directed/shells.gv")); importer.readFile(); QVERIFY2(importer.hasError() == false, importer.errorString().toStdString().c_str()); } void DotFileFormatTest::parseFileStates() { // create importer plugin DotFileFormat importer(this, QList()); importer.setFile(QUrl::fromLocalFile("directed/states.gv")); importer.readFile(); QVERIFY2(importer.hasError() == false, importer.errorString().toStdString().c_str()); } void DotFileFormatTest::parseFileStructs() { // create importer plugin DotFileFormat importer(this, QList()); importer.setFile(QUrl::fromLocalFile("directed/structs.gv")); importer.readFile(); QVERIFY2(importer.hasError() == false, importer.errorString().toStdString().c_str()); } void DotFileFormatTest::parseFileSwitch() { // create importer plugin DotFileFormat importer(this, QList()); importer.setFile(QUrl::fromLocalFile("directed/switch.gv")); importer.readFile(); QVERIFY2(importer.hasError() == false, importer.errorString().toStdString().c_str()); } void DotFileFormatTest::parseFileTable() { // create importer plugin DotFileFormat importer(this, QList()); importer.setFile(QUrl::fromLocalFile("directed/table.gv")); importer.readFile(); QEXPECT_FAIL("", "Parsing of interleaved XML tags not implemented", Continue); QVERIFY2(importer.hasError() == false, importer.errorString().toStdString().c_str()); } void DotFileFormatTest::parseFileTrain11() { // create importer plugin DotFileFormat importer(this, QList()); importer.setFile(QUrl::fromLocalFile("directed/train11.gv")); importer.readFile(); QVERIFY2(importer.hasError() == false, importer.errorString().toStdString().c_str()); } void DotFileFormatTest::parseFileTrapeziumlr() { // create importer plugin DotFileFormat importer(this, QList()); importer.setFile(QUrl::fromLocalFile("directed/trapeziumlr.gv")); importer.readFile(); QVERIFY2(importer.hasError() == false, importer.errorString().toStdString().c_str()); } void DotFileFormatTest::parseFileTree() { // create importer plugin DotFileFormat importer(this, QList()); importer.setFile(QUrl::fromLocalFile("directed/tree.gv")); importer.readFile(); QVERIFY2(importer.hasError() == false, importer.errorString().toStdString().c_str()); } void DotFileFormatTest::parseFileTriedds() { // create importer plugin DotFileFormat importer(this, QList()); importer.setFile(QUrl::fromLocalFile("directed/triedds.gv")); importer.readFile(); QVERIFY2(importer.hasError() == false, importer.errorString().toStdString().c_str()); } void DotFileFormatTest::parseFileTry() { // create importer plugin DotFileFormat importer(this, QList()); importer.setFile(QUrl::fromLocalFile("directed/try.gv")); importer.readFile(); QVERIFY2(importer.hasError() == false, importer.errorString().toStdString().c_str()); } void DotFileFormatTest::parseFileUnix() { // create importer plugin DotFileFormat importer(this, QList()); importer.setFile(QUrl::fromLocalFile("directed/unix.gv")); importer.readFile(); QVERIFY2(importer.hasError() == false, importer.errorString().toStdString().c_str()); GraphDocumentPtr document = importer.graphDocument(); // check that all of the node names were imported, and that there are no extra nodes QStringList nodeNames; nodeNames << "5th Edition" << "6th Edition" << "PWB 1.0" << "LSX" << "1 BSD" << "Mini Unix" << "Wollongong" << "Interdata" << "Unix/TS 3.0" << "PWB 2.0" << "7th Edition" << "8th Edition" << "32V" << "V7M" << "Ultrix-11" << "Xenix" << "UniPlus+" << "9th Edition" << "2 BSD" << "2.8 BSD" << "2.9 BSD" << "3 BSD" << "4 BSD" << "4.1 BSD" << "4.2 BSD" << "4.3 BSD" << "Ultrix-32" << "PWB 1.2" << "USG 1.0" << "CB Unix 1" << "USG 2.0" << "CB Unix 2" << "CB Unix 3" << "Unix/TS++" << "PDP-11 Sys V" << "USG 3.0" << "Unix/TS 1.0" << "TS 4.0" << "System V.0" << "System V.2" << "System V.3"; checkNodes(document, nodeNames); // check number of pointers QVERIFY(document->edges().count() == 49); } void DotFileFormatTest::parseFileUnix2() { // create importer plugin DotFileFormat importer(this, QList()); importer.setFile(QUrl::fromLocalFile("directed/unix2.gv")); importer.readFile(); QVERIFY2(importer.hasError() == false, importer.errorString().toStdString().c_str()); } void DotFileFormatTest::parseFileViewfile() { // create importer plugin DotFileFormat importer(this, QList()); importer.setFile(QUrl::fromLocalFile("directed/viewfile.gv")); importer.readFile(); QVERIFY2(importer.hasError() == false, importer.errorString().toStdString().c_str()); } void DotFileFormatTest::parseFileWorld() { // create importer plugin DotFileFormat importer(this, QList()); importer.setFile(QUrl::fromLocalFile("directed/world.gv")); importer.readFile(); QVERIFY2(importer.hasError() == false, importer.errorString().toStdString().c_str()); } void DotFileFormatTest::writeAndParseTest() { GraphDocumentPtr document = GraphDocument::create(); QMap nodes; // create simple graph with two nodes and one edge nodes.insert("a", Node::create(document)); nodes.insert("b", Node::create(document)); Edge::create(nodes["a"], nodes["b"]); // create testfile DotFileFormat dotFormat(this, QList()); QTemporaryFile testFile; testFile.open(); dotFormat.setFile(QUrl::fromLocalFile(testFile.fileName())); dotFormat.writeFile(document); // load document and test dotFormat.readFile(); QVERIFY(!dotFormat.hasError()); document = dotFormat.graphDocument(); QCOMPARE(document->nodes().count(), 2); QCOMPARE(document->edges().count(), 1); } QTEST_MAIN(DotFileFormatTest) diff --git a/libgraphtheory/fileformats/dot/dotgrammar.cpp b/libgraphtheory/fileformats/dot/dotgrammar.cpp index ba19ce99..61321353 100644 --- a/libgraphtheory/fileformats/dot/dotgrammar.cpp +++ b/libgraphtheory/fileformats/dot/dotgrammar.cpp @@ -1,491 +1,491 @@ /* This file is part of Rocs. 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 "dotgrammar.h" #include "dotgrammarhelper.h" #include "typenames.h" #include "graphdocument.h" #include "node.h" #include "logging_p.h" #include #include #include #include #include #include #include #include #include using namespace DotParser; using namespace GraphTheory; #define KGV_MAX_ITEMS_TO_LOAD std::numeric_limits::max() // get debug output for parser generator #define BOOST_SPIRIT_DEBUG 1 // define skipper for spaces, c style comments, and c++ style comments #define SKIPPER space | confix("//", eol)[*(char_ - eol)] | confix("/*", "*/")[*(char_ - "*/")] // workaround for linking boost namespace boost { void throw_exception(std::exception const &e) { qCCritical(GRAPHTHEORY_FILEFORMAT) << "Exception:" << e.what(); } } // create distinct parser for dot keywords namespace distinct { //[qi_distinct_encapsulation namespace spirit = boost::spirit; namespace standard = boost::spirit::standard; namespace repo = boost::spirit::repository; // Define metafunctions allowing to compute the type of the distinct() // and standard::char_() constructs namespace traits { // Metafunction allowing to get the type of any repository::distinct(...) // construct template struct distinct_spec : spirit::result_of::terminal {}; // Metafunction allowing to get the type of any standard::char_(...) construct template struct char_spec : spirit::result_of::terminal {}; }; // Define a helper function allowing to create a distinct() construct from // an arbitrary tail parser template inline typename traits::distinct_spec::type distinct_spec(Tail const& tail) { return repo::qi::distinct(tail); } // Define a helper function allowing to create a standard::char_() construct // from an arbitrary string representation template inline typename traits::char_spec::type char_spec(String const& str) { return standard::char_(str); } // the following constructs the type of a distinct_spec holding a // charset("0-9a-zA-Z_") as its tail parser typedef traits::char_spec::type charset_tag_type; typedef traits::distinct_spec::type keyword_tag_type; // Define a new Qi 'keyword' directive usable as a shortcut for a // repository::distinct(char_(std::string("0-9a-zA-Z_"))) std::string const keyword_spec("0-9a-zA-Z_"); keyword_tag_type const keyword = distinct_spec(char_spec(keyword_spec)); //] } -// The parser is implemented to fulfull exactly the DOT file specification. For details on the DOT +// The parser is implemented to fulfill exactly the DOT file specification. For details on the DOT // file format see /usr/share/doc/graphviz/html/info/lang.html or the graphviz website. The used // specification is as follows: // // graph : [ strict ] (graph | digraph) [ ID ] '{' stmt_list '}' // stmt_list : [ stmt [ ';' ] [ stmt_list ] ] // stmt : node_stmt // | edge_stmt // | attr_stmt // | ID '=' ID // | subgraph // attr_stmt : (graph | node | edge) attr_list // attr_list : '[' [ a_list ] ']' [ attr_list ] // a_list : ID [ '=' ID ] [ ',' ] [ a_list ] // edge_stmt : (node_id | subgraph) edgeRHS [ attr_list ] // edgeRHS : edgeop (node_id | subgraph) [ edgeRHS ] // node_stmt : node_id [ attr_list ] // node_id : ID [ port ] // port : ':' ID [ ':' compass_pt ] // | ':' compass_pt // subgraph : [ subgraph [ ID ] ] '{' stmt_list '}' // compass_pt : (n | ne | e | se | s | sw | w | nw | c | _) // // Definition for the ID non-terminal: any of // * any string of alphabetic ([a-zA-Z'200-'377]) characters, underscores ('_') or digits ([0-9]), not beginning with a digit; // * a numeral [-]?(.[0-9]+ | [0-9]+(.[0-9]*)? ); // * any double-quoted string ("...") possibly containing escaped quotes ('")1; // * an HTML string (<...>). // // Current problems of the parser: // * parsing of HTML/XML tags not completely implemented // * use of non ascii identifiers can cause parser errors namespace DotParser { namespace phx = boost::phoenix; using boost::phoenix::ref; using boost::phoenix::push_back; using boost::spirit::standard::alpha; using boost::spirit::standard::digit; using boost::spirit::standard::string; using boost::spirit::standard::space; using boost::spirit::qi::_1; using boost::spirit::qi::_val; using boost::spirit::qi::char_; using boost::spirit::qi::eol; using boost::spirit::qi::int_; using boost::spirit::qi::lexeme; using boost::spirit::qi::phrase_parse; using boost::spirit::qi::rule; using boost::spirit::qi::standard::space_type; using boost::spirit::repository::qi::confix; typedef BOOST_TYPEOF(SKIPPER) skipper_type; DotGraphParsingHelper* phelper = 0; template struct DotGrammar : boost::spirit::qi::grammar { DotGrammar() : DotGrammar::base_type(graph) { graph = -distinct::keyword["strict"][&setStrict] >> (distinct::keyword["graph"][&setUndirected] | distinct::keyword["digraph"][&setDirected]) >> -ID[&setGraphId] >> '{' >> stmt_list >> '}'; stmt_list = stmt >> -char_(';') >> -stmt_list; stmt = ( (ID[&attributeId] >> '=' >> ID[&valid])[&applyAttributeList] | attr_stmt | edge_stmt | node_stmt | subgraph ); attr_stmt = ( (distinct::keyword["graph"][phx::ref(phelper->attributed)="graph"] >> attr_list[&applyAttributeList])[&setGraphAttributes] | (distinct::keyword["node"][phx::ref(phelper->attributed)="node"] >> attr_list[&applyAttributeList]) | (distinct::keyword["edge"][phx::ref(phelper->attributed)="edge"] >> attr_list[&applyAttributeList]) ); attr_list = '[' >> -a_list >>']'; a_list = (ID[&attributeId] >> -('=' >> ID[&valid]))[&insertAttributeIntoAttributeList] >> -char_(',') >> -a_list; edge_stmt = ( (node_id[&edgebound] | subgraph) >> edgeRHS >> -(attr_list[phx::ref(phelper->attributed)="edge"]) )[&createAttributeList][&applyAttributeList][&createEdge][&removeAttributeList]; edgeRHS = edgeop[&checkEdgeOperator] >> (node_id[&edgebound] | subgraph) >> -edgeRHS; node_stmt = ( node_id[&createNode] >> -attr_list )[phx::ref(phelper->attributed)="node"][&createAttributeList][&applyAttributeList][&setNodeAttributes][&removeAttributeList]; node_id = ID >> -port; port = (':' >> ID >> -(':' >> compass_pt)) | (':' >> compass_pt); subgraph = -(distinct::keyword["subgraph"] >> -ID[&subGraphId]) >> char_('{')[&createSubGraph][&createAttributeList] >> stmt_list >> char_('}')[&leaveSubGraph][&removeAttributeList]; compass_pt = (distinct::keyword["n"] | distinct::keyword["ne"] | distinct::keyword["e"] | distinct::keyword["se"] | distinct::keyword["s"] | distinct::keyword["sw"] | distinct::keyword["w"] | distinct::keyword["nw"]); edgeop = string("->") | string("--"); ID = lexeme[ - // parse alpha-numberic sequence that is not a keyword + // parse alpha-numeric sequence that is not a keyword ( !(distinct::keyword["graph"] | distinct::keyword["edge"] | distinct::keyword["node"]) >> char_("a-zA-Z0-9") >> *char_("a-zA-Z0-9_") ) // parse number | (-char_('-') >> ('.' >> +digit) | (+digit >> -('.' >> *digit))) // parse anything that is in quotation marks | ('"' >> *(char_ - '"') >> '"') // parse XML attribute sequence | ('<' >> *(char_ - '>') >> '>') //TODO xml parser does not parse interlaced tags ]; } rule graph; rule ID; rule stmt_list; rule stmt; rule attr_stmt; rule attr_list; rule a_list; rule edge_stmt; rule edgeop; rule edgeRHS; rule node_stmt; rule node_id; rule port; rule subgraph; rule compass_pt; }; void leaveSubGraph() { if (!phelper) { return; } phelper->leaveSubGraph(); } void setStrict() { qCCritical(GRAPHTHEORY_FILEFORMAT) << "Graphviz \"strict\" keyword is not implemented."; } void setUndirected() { phelper->document->edgeTypes().first()->setDirection(EdgeType::Bidirectional); } void setDirected() { phelper->document->edgeTypes().first()->setDirection(EdgeType::Unidirectional); } void setGraphId(const std::string& str) { QString name = QString::fromStdString(str); qCCritical(GRAPHTHEORY_FILEFORMAT) << "Graph ID not supported, _not_ setting: " << name; //TODO not implemented } void attributeId(const std::string& str) { if (!phelper) { return; } // remove quotation marks QString id = QString::fromStdString(str); if (id.endsWith('"')) { id.remove(id.length()-1, 1); } if (id.startsWith('"')) { id.remove(0, 1); } phelper->attributeId = id; phelper->valid.clear(); } void subGraphId(const std::string& str) { if (!phelper) { return; } // remove quotation marks QString id = QString::fromStdString(str); if (id.endsWith('"')) { id.remove(id.length()-1, 1); } if (id.startsWith('"')) { id.remove(0, 1); } phelper->setSubGraphId(id); } void valid(const std::string& str) { if (!phelper) { return; } // remove quotation marks QString id = QString::fromStdString(str); if (id.endsWith('"')) { id.remove(id.length()-1, 1); } if (id.startsWith('"')) { id.remove(0, 1); } phelper->valid = id; } void insertAttributeIntoAttributeList() { if (!phelper) { return; } phelper->unprocessedAttributes.insert(phelper->attributeId, phelper->valid); } void createAttributeList() { if (!phelper) { return; } phelper->graphAttributeStack.push_back(phelper->graphAttributes); phelper->nodeAttributeStack.push_back(phelper->nodeAttributes); phelper->edgeAttributeStack.push_back(phelper->edgeAttributes); } void removeAttributeList() { if (!phelper) { return; } phelper->graphAttributes = phelper->graphAttributeStack.back(); phelper->graphAttributeStack.pop_back(); phelper->nodeAttributes = phelper->nodeAttributeStack.back(); phelper->nodeAttributeStack.pop_back(); phelper->edgeAttributes = phelper->edgeAttributeStack.back(); phelper->edgeAttributeStack.pop_back(); } void createNode(const std::string& str) { QString label = QString::fromStdString(str); if (!phelper || label.length() == 0) { return; } // remove quotation marks if (label.endsWith('"')) { label.remove(label.length()-1, 1); } if (label.startsWith('"')) { label.remove(0, 1); } if (!phelper->nodeMap.contains(label)) { phelper->createNode(label); } } void createSubGraph() { if (!phelper) { return; } phelper->createSubGraph(); } void setGraphAttributes() { if (!phelper) { return; } phelper->setDocumentAttributes(); } void setNodeAttributes() { if (!phelper) { return; } phelper->setNodeAttributes(); } void applyAttributeList() { if (!phelper) { return; } phelper->applyAttributedList(); } void checkEdgeOperator(const std::string& str) { if (!phelper) { return; } if (((phelper->document->edgeTypes().first()->direction() == EdgeType::Unidirectional) && (str.compare("->") == 0)) || ((phelper->document->edgeTypes().first()->direction() == EdgeType::Bidirectional) && (str.compare("--") == 0))) { return; } qCCritical(GRAPHTHEORY_FILEFORMAT) << "Error: incoherent edge direction relation" << endl; } void edgebound(const std::string& str) { if (!phelper) { return; } // remove quotation marks QString id = QString::fromStdString(str); if (id.endsWith('"')) { id.remove(id.length()-1, 1); } if (id.startsWith('"')) { id.remove(0, 1); } phelper->addEdgeBound(id); } void createEdge() { if (!phelper) { return; } phelper->createEdge(); } bool parseIntegers(const std::string& str, std::vector& v) { return phrase_parse(str.begin(), str.end(), // Begin grammar ( int_[phx::push_back(phx::ref(v), _1)] >> *(',' >> int_[phx::push_back(phx::ref(v), _1)]) ) , // End grammar space); } bool parse(const std::string& str, GraphDocumentPtr document) { delete phelper; phelper = new DotGraphParsingHelper; phelper->document = document; std::string input(str); std::string::iterator iter = input.begin(); DotGrammar r; if (phrase_parse(iter, input.end(), r, SKIPPER)) { qCDebug(GRAPHTHEORY_FILEFORMAT) << "Complete dot file was parsed successfully."; return true; } else { qCWarning(GRAPHTHEORY_FILEFORMAT) << "Dot file parsing failed. Unable to parse:"; qCDebug(GRAPHTHEORY_FILEFORMAT) << "///// FILE CONTENT BEGIN /////"; qCDebug(GRAPHTHEORY_FILEFORMAT) << QString::fromStdString(std::string(iter, input.end())); qCDebug(GRAPHTHEORY_FILEFORMAT) << "///// FILE CONTENT END /////"; } return false; } } diff --git a/libgraphtheory/fileformats/dot/dotgrammar.h b/libgraphtheory/fileformats/dot/dotgrammar.h index 18962c9a..d394b164 100644 --- a/libgraphtheory/fileformats/dot/dotgrammar.h +++ b/libgraphtheory/fileformats/dot/dotgrammar.h @@ -1,60 +1,60 @@ /* This file is part of Rocs. 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 . */ #ifndef DOTGRAMMAR_H #define DOTGRAMMAR_H #include "typenames.h" #include #include class Document; namespace DotParser { /** - * Parse the given string \p str that represents the textual respresentation of a + * Parse the given string \p str that represents the textual representation of a * graph in DOT/Graphviz format. The given document \p graphDoc must * be of plugin type "Graph. */ bool parse(const std::string& str, GraphTheory::GraphDocumentPtr document); bool parseIntegers(const std::string& str, std::vector& v); void setStrict(); void setUndirected(); void setDirected(); void setGraphId(const std::string &str); void attributeId(const std::string &str); void subGraphId(const std::string &str); void valid(const std::string &str); void insertAttributeIntoAttributeList(); void createAttributeList(); void removeAttributeList(); void createSubGraph(); void leaveSubGraph(); void createNode(const std::string &str); void setGraphAttributes(); void setSubGraphAttributes(char const* first, char const* last); void setNodeAttributes(); void applyAttributeList(); void checkEdgeOperator(const std::string &str); void edgebound(const std::string &str); void createEdge(); } #endif diff --git a/libgraphtheory/fileformats/dot/dotgrammarhelper.h b/libgraphtheory/fileformats/dot/dotgrammarhelper.h index abef2bce..28a55d11 100644 --- a/libgraphtheory/fileformats/dot/dotgrammarhelper.h +++ b/libgraphtheory/fileformats/dot/dotgrammarhelper.h @@ -1,92 +1,92 @@ /* This file is part of Rocs. Copyright 2006-2007 Gael de Chalendar Copyright 2012-2014 Andreas Cord-Landwehr KGraphViewer 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ -#ifndef DOT_GRAPHPARSINGHELPER_H -#define DOT_GRAPHPARSINGHELPER_H +#ifndef DOT_GRAMMARHELPER_H +#define DOT_GRAMMARHELPER_H #include "typenames.h" #include #include #include namespace DotParser { struct DotGraphParsingHelper { typedef QMap AttributesMap; DotGraphParsingHelper(); /** * Creates new data element and registers the identifier in data map. */ void createNode(const QString &name); /** * Creates new sub data structure and enters it. All future created data elements are add to * this sub data structure, until \see leaveSubDataStructure() is called. */ void createSubGraph(); /** * Leaves current group, i.e., leave current sub data structure and switches focus to ancestor * group or directly to parent data structures if left from all groups. */ void leaveSubGraph(); void setDocumentAttributes(); void setSubGraphAttributes(); void setSubGraphId(const QString &identifier); void setNodeAttributes(); void setEdgeAttributes(); /** * Generates a new attribute list from all unprocessed attributes and set the corresponding * attribute list for data-structure, data, or pointer. */ void applyAttributedList(); void createEdge(); void addEdgeBound(QString bound) { edgebounds.append(bound); } void setObjectAttributes(QObject *graphElement, const DotParser::DotGraphParsingHelper::AttributesMap &attributes); QString attributeId; QString valid; std::string attributed; //FIXME change to enum AttributesMap unprocessedAttributes; AttributesMap graphAttributes; AttributesMap nodeAttributes; AttributesMap edgeAttributes; QList< AttributesMap > graphAttributeStack; QList< AttributesMap > nodeAttributeStack; QList< AttributesMap > edgeAttributeStack; QStringList edgebounds; GraphTheory::GraphDocumentPtr document; GraphTheory::NodePtr currentNode; GraphTheory::EdgePtr currentEdge; QMap nodeMap; // for mapping node element ids }; } #endif diff --git a/libgraphtheory/fileformats/fileformatmanager.cpp b/libgraphtheory/fileformats/fileformatmanager.cpp index 1e074ca6..63aedba5 100644 --- a/libgraphtheory/fileformats/fileformatmanager.cpp +++ b/libgraphtheory/fileformats/fileformatmanager.cpp @@ -1,163 +1,163 @@ /* * 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 "fileformatmanager.h" #include "fileformatinterface.h" #include "logging_p.h" #include #include #include #include #include #include #include #include #include using namespace GraphTheory; class GraphTheory::FileFormatManagerPrivate { public: FileFormatManagerPrivate() : defaultGraphFilePlugin(nullptr) { } ~FileFormatManagerPrivate() { } QList backends; FileFormatInterface *defaultGraphFilePlugin; }; FileFormatManager::FileFormatManager() : d(new FileFormatManagerPrivate) { loadBackends(); } FileFormatManager::~FileFormatManager() { } QList FileFormatManager::backends() const { return d->backends; } QList FileFormatManager::backends(PluginType type) const { QList backends; foreach(FileFormatInterface *backend, d->backends) { switch(type) { case Import: if (backend->pluginCapability() == FileFormatInterface::ImportOnly || backend->pluginCapability() == FileFormatInterface::ImportAndExport) { backends.append(backend); } break; case Export: if (backend->pluginCapability() == FileFormatInterface::ExportOnly || backend->pluginCapability() == FileFormatInterface::ImportAndExport) { backends.append(backend); } break; default: break; } } return backends; } void FileFormatManager::loadBackends() { // remove all present backends foreach(FileFormatInterface *f, d->backends) { delete f; } d->backends.clear(); // dirs to check for plugins QStringList dirsToCheck; foreach (const QString &directory, QCoreApplication::libraryPaths()) { dirsToCheck << directory + QDir::separator() + "rocs/fileformats"; } // load plugins QPluginLoader loader; foreach (const QString &dir, dirsToCheck) { QVector metadataList = KPluginLoader::findPlugins(dir,[=](const KPluginMetaData &data){ return data.serviceTypes().contains("rocs/graphtheory/fileformat"); }); for (const auto &metadata : metadataList) { loader.setFileName(metadata.fileName()); qCDebug(GRAPHTHEORY_FILEFORMAT) << "Load Plugin: " << metadata.name(); if (!loader.load()) { qCCritical(GRAPHTHEORY_FILEFORMAT) << "Error while loading plugin: " << metadata.name(); } KPluginFactory *factory = KPluginLoader(loader.fileName()).factory(); FileFormatInterface *plugin = factory->create(this); d->backends.append(plugin); } } // display a QMessageBox if no plugins are found if (d->backends.empty()) { QMessageBox pluginErrorMessageBox; pluginErrorMessageBox.setWindowTitle(tr("Plugin Error")); pluginErrorMessageBox.setTextFormat(Qt::RichText); pluginErrorMessageBox.setText("Plugins could not be found in specified directories:
"+ dirsToCheck.join("
")+ "

Check " - "this link for further informations."); + "this link for further information."); pluginErrorMessageBox.setDefaultButton(QMessageBox::Close); pluginErrorMessageBox.exec(); exit(1); } // load static plugins d->defaultGraphFilePlugin = backendByExtension("graph2"); } FileFormatInterface * FileFormatManager::backendByExtension(const QString &ext) { QString suffix = ext.section('.', -1); // get suffix if (suffix.isEmpty()) { qCWarning(GRAPHTHEORY_FILEFORMAT) << "File does not contain extension, falling back to default file format"; return defaultBackend(); } foreach(FileFormatInterface * p, d->backends) { if (p->extensions().join(";").contains(suffix, Qt::CaseInsensitive)) { return p; } } return nullptr; } FileFormatInterface * FileFormatManager::defaultBackend() { return d->defaultGraphFilePlugin; } diff --git a/libgraphtheory/fileformats/gml/gmlgrammar.cpp b/libgraphtheory/fileformats/gml/gmlgrammar.cpp index 2b224344..a2eaa2f6 100644 --- a/libgraphtheory/fileformats/gml/gmlgrammar.cpp +++ b/libgraphtheory/fileformats/gml/gmlgrammar.cpp @@ -1,144 +1,144 @@ /* This file is part of Rocs. 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 "gmlgrammar.h" #include "gmlgrammarhelper.h" #include "logging_p.h" #define KGV_MAX_ITEMS_TO_LOAD std::numeric_limits::max() #define BOOST_SPIRIT_DEBUG 1 using namespace GraphTheory; // workaround for linking boost namespace boost { void throw_exception(std::exception const &e) { qCCritical(GRAPHTHEORY_FILEFORMAT) << "Exception:" << e.what(); } } namespace GmlParser { GmlGrammarHelper *phelper = nullptr; std::string lastKey = std::string(); QObject * lastInserted = nullptr; void beginList() { phelper->startList(QString::fromStdString(lastKey)); } void endList() { phelper->endList(); } void gotKey(const std::string& key) { lastKey = key.c_str(); // QString k = key.c_str(); // if (k.compare("dataType", Qt::CaseInsensitive) == 0){ // qCDebug(GRAPHTHEORY_FILEFORMAT) << "create a graph"; // actualGraph = document->addGraph("new"); // lastInserted = actualGraph; // }else if (k.compare("node", Qt::CaseInsensitive) == 0){ // qCDebug(GRAPHTHEORY_FILEFORMAT) << "create a node"; // actualNode = actualGraph->addNode("new"); // lastInserted = actualNode; // }else if (k.compare("edge", Qt::CaseInsensitive) == 0){ // qCDebug(GRAPHTHEORY_FILEFORMAT) << "create a edge"; // }else { -// qCDebug(GRAPHTHEORY_FILEFORMAT) << "Process atribute " << k; +// qCDebug(GRAPHTHEORY_FILEFORMAT) << "Process attribute " << k; // lastKey = key; // } } void gotValue(const std::string& Value) { if (Value.empty()) { return; //end of the list. } else { phelper->setAttribute(QString::fromStdString(lastKey), QString::fromStdString(Value)); // if (lastInserted){ if (!lastInserted) { qCCritical(GRAPHTHEORY_FILEFORMAT) << "Cannot specify data node value: internal error"; return; } if (lastKey == "id" && lastInserted){ lastInserted->setProperty("name", Value.c_str()); phelper->nodeMap.insert(QString::fromStdString(Value), phelper->currentNode); } // lastInserted->setProperty(lastKey.c_str(), Value.c_str()); // }else{ // qCDebug(GRAPHTHEORY_FILEFORMAT) << "ignoring keyvalue: "<< lastKey.c_str() << Value.c_str(); // } } } void t() { std::cout << "Found whitespace.\n"; } void t1(const std::string &key) { std::cout << "Found " << key << ".\n"; } bool parse(const QString& content, GraphDocumentPtr document) { QString tmpContent = content; unsigned result; phelper = new GmlGrammarHelper; phelper->document = document; typedef std::string::const_iterator iterator_type; typedef GmlParser::roman roman; roman roman_parser; // Our grammar int index; while ((index = tmpContent.indexOf('#')) != -1) { tmpContent.remove(index, tmpContent.indexOf('\n', index) - index); } std::string str = tmpContent.toStdString(); iterator_type iter = str.begin(); iterator_type end = str.end(); bool r = parse(iter, end, roman_parser, result); if (r && iter == end) { std::cout << "-------------------------\n"; std::cout << "Parsing succeeded\n"; std::cout << "result = " << result << std::endl; std::cout << "-------------------------\n"; } else { std::string rest(iter, end); std::cout << "-------------------------\n"; std::cout << "Parsing failed\n"; std::cout << "stopped at: \": " << rest << "\"\n"; std::cout << "-------------------------\n"; } delete phelper; return r; } } diff --git a/libgraphtheory/fileformats/gml/gmlgrammarhelper.cpp b/libgraphtheory/fileformats/gml/gmlgrammarhelper.cpp index c4334bf3..47bcb9c6 100644 --- a/libgraphtheory/fileformats/gml/gmlgrammarhelper.cpp +++ b/libgraphtheory/fileformats/gml/gmlgrammarhelper.cpp @@ -1,193 +1,193 @@ /* This file is part of Rocs. Copyright 2006-2007 Gael de Chalendar Copyright 2012 Andreas Cord-Landwehr Rocs 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include "gmlgrammarhelper.h" #include "gmlgrammar.h" #include "graphdocument.h" #include "node.h" #include "edge.h" #include "logging_p.h" #include extern GmlParser::GmlGrammarHelper *phelper; using namespace GraphTheory; namespace GmlParser { GmlGrammarHelper::GmlGrammarHelper(): edgeSource(), edgeTarget(), currentState(begin) { document.reset(); currentNode.reset(); currentEdge.reset(); } void GmlGrammarHelper::startList(const QString& key) { qCDebug(GRAPHTHEORY_FILEFORMAT) << "starting a list with key:" << key; if (currentState == begin && key.compare("graph", Qt::CaseInsensitive) == 0) { createGraph(); return; } else if (currentState == graph) { if (key.compare("node", Qt::CaseInsensitive) == 0) { createNode(); return; } else if (key.compare("edge", Qt::CaseInsensitive) == 0) { createEdge(); return; } } attributeStack.append(key); } void GmlGrammarHelper::endList() { if (!attributeStack.isEmpty()) { attributeStack.removeLast(); return; } switch (currentState) { case begin: qCDebug(GRAPHTHEORY_FILEFORMAT) << "Ending a list without begin a item??"; break; case node: currentNode.reset(); currentState = graph; break; case edge: currentEdge.reset(); currentState = graph; break; case graph: document.reset(); currentState = begin; break; } } const QString GmlGrammarHelper::processKey(const QString& key) { QString ret = key; if (key.compare("id", Qt::CaseInsensitive) == 0) { ret = "name"; } return ret; } void GmlGrammarHelper::setAttribute(const QString& key, const QString& value) { - qCDebug(GRAPHTHEORY_FILEFORMAT) << "Setting attibute " << key; + qCDebug(GRAPHTHEORY_FILEFORMAT) << "Setting attribute " << key; switch (currentState) { case begin: break; case graph: if (!attributeStack.isEmpty()) { QString joined = attributeStack.join("."); joined.append('.').append(key); // document->setDynamicProperty(joined, value); } else { - qCDebug(GRAPHTHEORY_FILEFORMAT) << "seting property to graph" << key << value; + qCDebug(GRAPHTHEORY_FILEFORMAT) << "setting property to graph" << key << value; // if (!currentGraph->setProperty(processKey(key).toAscii(),value)){ // document->addDynamicProperty(processKey(key), value); //is a dinamic property // } } break; case edge: if (!attributeStack.isEmpty()) { //is a list of properties of edge QString joined = attributeStack.join("."); joined.append('.').append(key); if (currentEdge) { currentEdge->setDynamicProperty(joined, value); } else { edgeAttributes.insert(joined, value); } } else if (key.compare("source", Qt::CaseInsensitive) == 0) { // search for source.... edgeSource = value; createEdge(); } else if (key.compare("target", Qt::CaseInsensitive) == 0) { // .... and target edgeTarget = value; createEdge(); } else if (currentEdge) { //if edge was created. // if(!currentEdge->setProperty(processKey(key).toAscii(),value)){ qCDebug(GRAPHTHEORY_FILEFORMAT) << "inserting edge key: " << key; currentEdge->setDynamicProperty(processKey(key), value); // // } } else { qCDebug(GRAPHTHEORY_FILEFORMAT) << "Saving edge key: " << key; edgeAttributes.insert(processKey(key), value); //store to be inserted later } break; case node: if (!attributeStack.isEmpty()) { QString joined = attributeStack.join("."); joined.append('.').append(key); currentNode->setProperty(joined.toUtf8(), value); } else { - qCDebug(GRAPHTHEORY_FILEFORMAT) << "seting property to node" << key << value; + qCDebug(GRAPHTHEORY_FILEFORMAT) << "setting property to node" << key << value; // if(!currentNode->setProperty(processKey(key).toAscii(),value)){ currentNode->setDynamicProperty(processKey(key), value); // } } break; } } void GmlGrammarHelper::createGraph() { if (currentState == begin) { document = GraphDocument::create(); currentState = graph; } } void GmlGrammarHelper::createNode() { if (currentState == graph) { qCDebug(GRAPHTHEORY_FILEFORMAT) << "Creating a node"; currentState = node; currentNode = Node::create(document); } } void GmlGrammarHelper::createEdge() { if (!edgeSource.isEmpty() && !edgeTarget.isEmpty()) { qCDebug(GRAPHTHEORY_FILEFORMAT) << "Creating a edge"; currentState = edge; if (!nodeMap.contains(edgeSource) || !nodeMap.contains(edgeTarget)) { qCCritical(GRAPHTHEORY_FILEFORMAT) << "No edge created: end points were not created"; return; } currentEdge = Edge::create(nodeMap[edgeSource], nodeMap[edgeTarget]); edgeSource.clear();; edgeTarget.clear(); while (!edgeAttributes.isEmpty()) { QString property = edgeAttributes.keys().at(0); currentEdge->setDynamicProperty(property, edgeAttributes.value(property)); edgeAttributes.remove(property); } } else if (currentState == graph) { qCDebug(GRAPHTHEORY_FILEFORMAT) << "changing state Edge"; currentState = edge; currentEdge.reset(); } } } diff --git a/libgraphtheory/fileformats/rocs1/autotests/testrocs1fileformat.cpp b/libgraphtheory/fileformats/rocs1/autotests/testrocs1fileformat.cpp index 2ef5d1d8..ec9bdbef 100644 --- a/libgraphtheory/fileformats/rocs1/autotests/testrocs1fileformat.cpp +++ b/libgraphtheory/fileformats/rocs1/autotests/testrocs1fileformat.cpp @@ -1,142 +1,142 @@ /* * 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 +#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); } } void TestRocs1FileFormat::serializeUnserializeTypesTest() { GraphDocumentPtr document = GraphDocument::create(); QMap nodes; NodeTypePtr nodeType2 = NodeType::create(document); EdgeTypePtr edgeType2 = EdgeType::create(document); // 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(); QCOMPARE(document->nodeTypes().count(), 2); QCOMPARE(document->edgeTypes().count(), 2); } //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.h b/libgraphtheory/fileformats/rocs1/rocs1fileformat.h index 665e2029..cc2e4aa2 100644 --- a/libgraphtheory/fileformats/rocs1/rocs1fileformat.h +++ b/libgraphtheory/fileformats/rocs1/rocs1fileformat.h @@ -1,151 +1,151 @@ /* This file is part of Rocs. Copyright 2012 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 . */ #ifndef ROCS1FILEFORMAT_H #define ROCS1FILEFORMAT_H #include "fileformats/fileformatinterface.h" namespace GraphTheory { class Rocs1FileFormatPrivate; /** \brief class RocsGraphFileFormatPlugin: Import and Export Plugin for internal graph format. * * A Document Internal format may look like this: * * # '#'s are comments. * * [Document Properties] # Canvas Size and Data Structure initialization. * Width : [integer] * Height : [integer] * DataStructurePlugin : [string] * * # data types * [DataType Identifier1] * Name : [string] * IconName : [string] * Color : [RGB in hex] * * # pointer types * [PointerType Identifier1] * Name : [string] * Color : [RGB in hex] * * # Data Structure Definitions. * # Data Structures are layered, so there can be N of them. * # Every Data and Pointer below a declaration of a DataStructure * # belongs to that data structure. * * [DataStructure Name1] * * DataColor : [RGB in hex] # Color of newly created Data elements ( Nodes ) * Pointercolor : [RBG in Hex] # Color of newly created Data elements ( Nodes ) - * name : [string] # Name of the data structure acessible in the scripting interface. + * name : [string] # Name of the data structure accessible in the scripting interface. * * visibility : [bool] # is this DataStructure visible? * * ShowNamesInData : [bool] # should the canvas show the name of the data on the screen? * ShowNamesInNPointers : [bool] # ↑ * ShowValuesInData : [bool] # ↑ * ShowValuesInPointers : [bool] # ↑ * * PluginDefinedProperty1 : propertyValue; # the plugins can define other properties for the data structure. * PluginDefinedProperty1 : propertyValue; * PluginDefinedProperty1 : propertyValue; * * UserDefinedProperty1 : propertyValue1 # the user can define other properties for the data structure. * UserDefinedProperty2 : propertyValue2 * UserDefinedProperty3 : propertyValue3 * * [Data 1] * type : [int] * x : [real] * y : [real] * color : [string in "0xFFFFFF" format] * name : [string] * value : [string] * size : [float] * * property1 : propertyValue1 * property2 : propertyValue2 * property3 : propertyValue3 * * [Data 2] <- same as above. * type : [int] * x : [integer] * y : [integer] * color : [string in "0xFFFFFF" format] * name : [string] * value : [string] * size : [float] * * property1 : propertyValue1 * property2 : propertyValue2 * property3 : propertyValue3 * * [Pointer 1 -> 2] * type : [int] * name : [string] * value : [string] * style : [string] * color : [string in "0xFFFFFF" format] * width : [float] * * property1 : propertyValue1 * property2 : propertyValue2 * property3 : propertyValue3 */ class Rocs1FileFormat : public FileFormatInterface { Q_OBJECT public: explicit Rocs1FileFormat(QObject *parent, const QList< QVariant >&); ~Rocs1FileFormat(); /** * File extensions that are common for this file type. */ const QStringList extensions() const Q_DECL_OVERRIDE; /** * Writes given graph document to formerly specified file \see setFile(). * \param graph is graphDocument to be serialized */ void writeFile(GraphDocumentPtr graph) Q_DECL_OVERRIDE; /** * Open given file and imports it into internal format. * \param file is url of a local file */ void readFile() Q_DECL_OVERRIDE; private: QString serialize(GraphDocumentPtr document); void serializeProperties(QObject *o); Rocs1FileFormatPrivate *d; }; } #endif diff --git a/libgraphtheory/fileformats/tgf/autotests/testtgffileformat.cpp b/libgraphtheory/fileformats/tgf/autotests/testtgffileformat.cpp index 3a6611d8..5ca72437 100644 --- a/libgraphtheory/fileformats/tgf/autotests/testtgffileformat.cpp +++ b/libgraphtheory/fileformats/tgf/autotests/testtgffileformat.cpp @@ -1,86 +1,86 @@ /* * 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 "testtgffileformat.h" #include "../tgffileformat.h" #include "fileformats/fileformatinterface.h" #include "graphdocument.h" #include "node.h" #include "edge.h" -#include +#include using namespace GraphTheory; TestTgfFileFormat::TestTgfFileFormat() { } void TestTgfFileFormat::serializeUnserializeTest() { GraphDocumentPtr document = GraphDocument::create(); document->nodeTypes().first()->addDynamicProperty("label"); document->edgeTypes().first()->addDynamicProperty("label"); QMap dataList; // Creates a simple Graph with 5 data elements and connect them with pointers. dataList.insert("a", Node::create(document)); dataList["a"]->setDynamicProperty("label", "first node"); dataList.insert("b", Node::create(document)); dataList["b"]->setDynamicProperty("label", "b"); dataList.insert("c", Node::create(document)); dataList["c"]->setDynamicProperty("label", "c"); dataList.insert("d", Node::create(document)); dataList["d"]->setDynamicProperty("label", "d"); dataList.insert("e", Node::create(document)); dataList["e"]->setDynamicProperty("label", "e"); Edge::create(dataList["a"], dataList["b"])->setDynamicProperty("label", "test value"); Edge::create(dataList["b"], dataList["c"]); Edge::create(dataList["c"], dataList["d"]); Edge::create(dataList["d"], dataList["e"]); Edge::create(dataList["e"], dataList["a"]); // create exporter plugin TgfFileFormat serializer(this, QList()); serializer.setFile(QUrl::fromLocalFile("test.tgf")); serializer.writeFile(document); QVERIFY(serializer.hasError() == false); // create importer TgfFileFormat importer(this, QList()); importer.setFile(QUrl::fromLocalFile("test.tgf")); importer.readFile(); QVERIFY(importer.hasError() == false); QVERIFY(importer.isGraphDocument()); document = importer.graphDocument(); // test imported values QVERIFY2(document->nodes().size() == 5, "ERROR: Number of data is not 5 "); QVERIFY2(document->edges().size() == 5, "ERROR: Number of pointers is not 5 "); foreach(NodePtr node, document->nodes()) { QCOMPARE(node->outEdges().size(), 1); QCOMPARE(node->inEdges().size(), 1); QCOMPARE(node->edges().count(), 2); } QCOMPARE(document->nodes().first()->dynamicProperty("label").toString(), QString("first node")); QCOMPARE(document->edges().first()->dynamicProperty("label").toString(), QString("test value")); } QTEST_MAIN(TestTgfFileFormat); diff --git a/libgraphtheory/fileformats/tgf/tgffileformat.h b/libgraphtheory/fileformats/tgf/tgffileformat.h index 74c202c7..fc40152a 100644 --- a/libgraphtheory/fileformats/tgf/tgffileformat.h +++ b/libgraphtheory/fileformats/tgf/tgffileformat.h @@ -1,77 +1,77 @@ /* 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 . */ #ifndef TGFFILEFORMAT_H #define TGFFILEFORMAT_H #include "fileformats/fileformatinterface.h" namespace GraphTheory { /** \brief class TgfFileFormatPlugin: Import and Export Plugin for TGF * * This plugin class allows reading and writing of Trivial Graph Format (TGF) files. * TGF is a simple file format that can store directed graphs by a list of nodes and list of edges. * Each node and each edge can have exactly one label. * * Format Specification: * - The file starts with a list of nodes (one node per line), followed by a line with the only * character "#", followed by a list of edges (one edge per line). * - A node consists of an integer (identifier), followed by a space, followed by an arbitrary string. * - An edge consists of two integers (identifiers) separated by a space, followed by a space, - * followed by an arbitray string. It is assumed that the directed edge points from the first + * followed by an arbitrary string. It is assumed that the directed edge points from the first * identifier to the second identifier. */ class TgfFileFormat : public FileFormatInterface { Q_OBJECT public: explicit TgfFileFormat(QObject *parent, const QList< QVariant >&); ~TgfFileFormat(); /** * File extensions that are common for this file type. */ const QStringList extensions() const Q_DECL_OVERRIDE; /** * Writes given graph document to formerly specified file \see setFile(). * \param graph is graphDocument to be serialized */ void writeFile(GraphDocumentPtr graph) Q_DECL_OVERRIDE; /** * Open given file and imports it into internal format. * \param file is url of a local file */ void readFile() Q_DECL_OVERRIDE; private: enum ReadMode { Nodes, Edges }; }; } #endif diff --git a/libgraphtheory/fileformats/tikz/autotests/testtikzfileformat.cpp b/libgraphtheory/fileformats/tikz/autotests/testtikzfileformat.cpp index 94706643..d94841ef 100644 --- a/libgraphtheory/fileformats/tikz/autotests/testtikzfileformat.cpp +++ b/libgraphtheory/fileformats/tikz/autotests/testtikzfileformat.cpp @@ -1,65 +1,65 @@ /* * 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 "testtikzfileformat.h" #include "../tikzfileformat.h" #include "fileformats/fileformatinterface.h" #include "graphdocument.h" #include "node.h" #include "edge.h" -#include +#include using namespace GraphTheory; TestTikzFileFormat::TestTikzFileFormat() { } void TestTikzFileFormat::serializeTest() { 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"]); // create exporter plugin TikzFileFormat serializer(this, QList()); serializer.setFile(QUrl::fromLocalFile("test.tgf")); serializer.writeFile(document); QVERIFY(serializer.hasError() == false); } QTEST_MAIN(TestTikzFileFormat); diff --git a/libgraphtheory/fileformats/tikz/tikzfileformat.cpp b/libgraphtheory/fileformats/tikz/tikzfileformat.cpp index d58331b0..46f372d4 100644 --- a/libgraphtheory/fileformats/tikz/tikzfileformat.cpp +++ b/libgraphtheory/fileformats/tikz/tikzfileformat.cpp @@ -1,147 +1,147 @@ /* * 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 "tikzfileformat.h" #include "fileformats/fileformatinterface.h" #include "graphdocument.h" #include "node.h" #include "edge.h" #include "logging_p.h" #include #include #include #include #include #include using namespace GraphTheory; K_PLUGIN_FACTORY_WITH_JSON( FilePluginFactory, "tikzfileformat.json", registerPlugin();) TikzFileFormat::TikzFileFormat(QObject* parent, const QList< QVariant >&) : FileFormatInterface("rocs_tikzfileformat", parent) { } TikzFileFormat::~TikzFileFormat() { } const QStringList TikzFileFormat::extensions() const { return QStringList() << i18n("TikZ (PGF) Format (%1)", QString("*.pgf")); } FileFormatInterface::PluginType TikzFileFormat::pluginCapability() const { return FileFormatInterface::ExportOnly; } void TikzFileFormat::readFile() { qCWarning(GRAPHTHEORY_FILEFORMAT) << "This plugin cannot import documents."; setError(NotSupportedOperation); } void TikzFileFormat::writeFile(GraphDocumentPtr graph) { // TODO allow selection which data structure shall be exported QFile fileHandle(file().toLocalFile()); if (!fileHandle.open(QFile::WriteOnly | QFile::Text)) { setError(FileIsReadOnly, i18n("Could not open file \"%1\" in write mode: %2", file().fileName(), fileHandle.errorString())); return; } QTextStream out(&fileHandle); QMap nodeTypeIdMap; QMap edgeTypeIdMap; // use this value to scale all coordinates int resize = 50; // start picture out << "\\begin{tikzpicture}\n"; // style information out << "\\tikzstyle{value} = [font=\\small]\n"; for (int i = 0; i < graph->nodeTypes().length(); ++i) { nodeTypeIdMap.insert(graph->nodeTypes().at(i), i); // TODO set type specific style information QString dataTypeStyle = QString("\\tikzstyle{nodetype%1}=[circle,thin,fill=black!25,minimum size=20pt,inner sep=0pt]"). arg(i); out << dataTypeStyle << "\n"; } for (int i = 0; i < graph->edgeTypes().length(); ++i) { EdgeTypePtr type = graph->edgeTypes().at(i); edgeTypeIdMap.insert(type, i); // set style attributes QString styleAttributes = QString("draw,thick"); // direction if (type->direction() == EdgeType::Unidirectional) { styleAttributes.append(",->"); } else { styleAttributes.append(",-"); } // set style QString pointerTypeStyle = QString("\\tikzstyle{edgetype%1} = [%2]").arg(i).arg(styleAttributes); out << pointerTypeStyle << "\n"; } // export data elements - // y-axis is mirrowed in tikz-format + // y-axis is mirrored in tikz-format foreach(NodeTypePtr type, graph->nodeTypes()) { foreach(NodePtr node, graph->nodes(type)) { QString nodeStr = QString("\\node[nodetype%1] (%2) at (%3,%4) [label=left:%5] {%6};"). arg(nodeTypeIdMap[type]). arg(node->id()). arg(node->x()/resize). arg(node->y()*(-1)/resize). arg(node->property("name").toString()). arg(node->property("value").toString()); out << nodeStr; out << '\n'; } } // export pointers foreach(EdgeTypePtr type, graph->edgeTypes()) { foreach(EdgePtr edge, graph->edges(type)) { QString edgeStr = QString("\\path[edgetype%1] (%2) -- node[value] {%3} (%4);"). arg(edgeTypeIdMap[type]). arg(edge->from()->id()). arg(edge->property("value").toString()). arg(edge->to()->id()); out << edgeStr; out << '\n'; } } out << "\\end{tikzpicture}\n"; setError(None); } #include "tikzfileformat.moc" diff --git a/libgraphtheory/kernel/documentwrapper.cpp b/libgraphtheory/kernel/documentwrapper.cpp index 232ccb4d..e116e244 100644 --- a/libgraphtheory/kernel/documentwrapper.cpp +++ b/libgraphtheory/kernel/documentwrapper.cpp @@ -1,238 +1,238 @@ /* * Copyright 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 "documentwrapper.h" #include "nodewrapper.h" #include "edgewrapper.h" #include "graphdocument.h" #include "nodetype.h" #include "edge.h" #include #include using namespace GraphTheory; DocumentWrapper::DocumentWrapper(GraphDocumentPtr document, QScriptEngine *engine) : m_document(document) , m_engine(engine) { foreach (NodePtr node, document->nodes()) { registerWrapper(node); } foreach (EdgePtr edge, document->edges()) { registerWrapper(edge); } connect(document.data(), &GraphDocument::nodeAboutToBeAdded, this, static_cast(&DocumentWrapper::registerWrapper)); connect(document.data(), &GraphDocument::edgeAboutToBeAdded, this, static_cast(&DocumentWrapper::registerWrapper)); } DocumentWrapper::~DocumentWrapper() { qDeleteAll(m_edgeMap); qDeleteAll(m_nodeMap); } QScriptEngine* DocumentWrapper::engine() const { return m_engine; } void DocumentWrapper::registerWrapper(NodePtr node) { if (m_nodeMap.contains(node)) { return; } NodeWrapper *wrapper = new NodeWrapper(node, this); m_nodeMap.insert(node, wrapper); connect(wrapper, &NodeWrapper::message, this, &DocumentWrapper::message); return; } void DocumentWrapper::registerWrapper(EdgePtr edge) { if (m_edgeMap.contains(edge)) { return; } EdgeWrapper *wrapper = new EdgeWrapper(edge, this); m_edgeMap.insert(edge, wrapper); connect(wrapper, &EdgeWrapper::message, this, &DocumentWrapper::message); return; } NodeWrapper * DocumentWrapper::nodeWrapper(NodePtr node) const { Q_ASSERT(m_nodeMap.contains(node)); return m_nodeMap.value(node); } EdgeWrapper * DocumentWrapper::edgeWrapper(EdgePtr edge) const { Q_ASSERT(m_edgeMap.contains(edge)); return m_edgeMap.value(edge); } QScriptValue DocumentWrapper::node(int id) const { //TODO average access time is linear, which might drastically be improved by // using proper caching mechanisms at Document objects for (auto const &node : m_document->nodes()) { if (node->id() == id) { return m_engine->newQObject(nodeWrapper(node), QScriptEngine::QtOwnership, QScriptEngine::AutoCreateDynamicProperties); } } QString command = QString("Document.node(%1)").arg(id); emit message(i18nc("@info:shell", "%1: no node with ID %2 registered", command, id), Kernel::ErrorMessage); return QScriptValue(); } QScriptValue DocumentWrapper::nodes() const { NodeList nodes = m_document->nodes(); QScriptValue array = m_engine->newArray(nodes.length()); for (int i = 0; i < nodes.length(); ++i) { QScriptValue nodeScriptValue = m_engine->newQObject(nodeWrapper(nodes.at(i)), QScriptEngine::QtOwnership, QScriptEngine::AutoCreateDynamicProperties); array.setProperty(i, nodeScriptValue); } return array; } QScriptValue DocumentWrapper::nodes(int type) const { NodeTypePtr typePtr; foreach (NodeTypePtr typeTest, m_document->nodeTypes()) { if (typeTest->id() == type) { typePtr = typeTest; break; } } if (!typePtr) { QString command = QString("Document.nodes(%1)").arg(type); emit message(i18nc("@info:shell", "%1: node type ID %2 not registered", command, type), Kernel::ErrorMessage); return m_engine->newArray(); } NodeList nodes = m_document->nodes(typePtr); QScriptValue array = m_engine->newArray(nodes.length()); for (int i = 0; i < nodes.length(); ++i) { QScriptValue nodeScriptValue = m_engine->newQObject(nodeWrapper(nodes.at(i)), QScriptEngine::QtOwnership, QScriptEngine::AutoCreateDynamicProperties); array.setProperty(i, nodeScriptValue); } return array; } QScriptValue DocumentWrapper::edges() const { EdgeList edges = m_document->edges(); QScriptValue array = m_engine->newArray(edges.length()); for (int i = 0; i < edges.length(); ++i) { QScriptValue edgeScriptValue = m_engine->newQObject(edgeWrapper(edges.at(i)), QScriptEngine::QtOwnership, QScriptEngine::AutoCreateDynamicProperties); array.setProperty(i, edgeScriptValue); } return array; } QScriptValue DocumentWrapper::edges(int type) const { EdgeTypePtr typePtr; foreach (EdgeTypePtr typeTest, m_document->edgeTypes()) { if (typeTest->id() == type) { typePtr = typeTest; break; } } if (!typePtr) { QString command = QString("Document.edges(%1)").arg(type); emit message(i18nc("@info:shell", "%1: edge type ID %2 not registered", command, type), Kernel::ErrorMessage); return m_engine->newArray(); } EdgeList edges = m_document->edges(typePtr); QScriptValue array = m_engine->newArray(edges.length()); for (int i = 0; i < edges.length(); ++i) { QScriptValue edgeScriptValue = m_engine->newQObject(edgeWrapper(edges.at(i)), QScriptEngine::QtOwnership, QScriptEngine::AutoCreateDynamicProperties); array.setProperty(i, edgeScriptValue); } return array; } QScriptValue DocumentWrapper::createNode(int x, int y) { NodePtr node = Node::create(m_document); node->setX(x); node->setY(y); return m_engine->newQObject(nodeWrapper(node), QScriptEngine::QtOwnership, QScriptEngine::AutoCreateDynamicProperties); } QScriptValue DocumentWrapper::createEdge(NodeWrapper *from, NodeWrapper *to) { if (!from) { QString command = QString("Document.createEdge(from, to)"); emit message(i18nc("@info:shell", "%1: \"from\" is not a valid node object", command), Kernel::ErrorMessage); return QScriptValue(); } if (!to) { QString command = QString("Document.createEdge(from, to)"); emit message(i18nc("@info:shell", "%1: \"to\" is not a valid node object", command), Kernel::ErrorMessage); return QScriptValue(); } EdgePtr edge = Edge::create(from->node(), to->node()); return m_engine->newQObject(edgeWrapper(edge), QScriptEngine::QtOwnership, QScriptEngine::AutoCreateDynamicProperties); } void DocumentWrapper::remove(NodeWrapper *node) { if (!node) { QString command = QString("Document.remove(node)"); emit message(i18nc("@info:shell", "%1: \"node\" is not a valid node object", command), Kernel::ErrorMessage); return; } - // note: The NodeWrapper object explicitly is not removed freom m_nodeMap and by this the node object is not removed. + // note: The NodeWrapper object explicitly is not removed from m_nodeMap and by this the node object is not removed. // This has the benefit to not taking care validity of Node, NodeWrapper and its QScriptObject, but on the downside // leads to much used memory, that is only freed after run. // TODO: we need a mechanism that carefully implements on-the-fly object deletions node->node()->destroy(); } void DocumentWrapper::remove(EdgeWrapper *edge) { if (!edge) { QString command = QString("Document.remove(edge)"); emit message(i18nc("@info:shell", "%1: \"edge\" is not a valid edge object", command), Kernel::ErrorMessage); return; } - // note: The EdgeWrapper object explicitly is not removed freom m_edgeMap and by this the edge object is not removed. + // note: The EdgeWrapper object explicitly is not removed from m_edgeMap and by this the edge object is not removed. // This has the benefit to not taking care validity of Edge, EdgeWrapper and its QScriptObject, but on the downside // leads to much used memory, that is only freed after run. // TODO: we need a mechanism that carefully implements on-the-fly object deletions edge->edge()->destroy(); } diff --git a/libgraphtheory/kernel/edgewrapper.h b/libgraphtheory/kernel/edgewrapper.h index e7cb83c7..367c33e5 100644 --- a/libgraphtheory/kernel/edgewrapper.h +++ b/libgraphtheory/kernel/edgewrapper.h @@ -1,93 +1,93 @@ /* * Copyright 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 . */ #ifndef EDGEWRAPPER_H #define EDGEWRAPPER_H #include "graphtheory_export.h" #include "kernel.h" #include "typenames.h" #include "node.h" #include "graphdocument.h" #include #include class QEvent; namespace GraphTheory { class EdgeWrapperPrivate; class DocumentWrapper; class NodeWrapper; /** * \class EdgeWrapper * Wraps EdgePtr to be accessible via QtScript. All properties of the node object are available - * as commong QObject properties. + * as common QObject properties. */ class EdgeWrapper : public QObject { Q_OBJECT Q_PROPERTY(int type READ type WRITE setType NOTIFY typeChanged) public: EdgeWrapper(EdgePtr edge, DocumentWrapper *documentWrapper); virtual ~EdgeWrapper(); EdgePtr edge() const; /** * @return EdgeType::id of corresponding node */ int type() const; /** * Set EdgeType of corresponding node by specifying its ID by @p typeId. * If @p typeId does not name the ID of any NodeType, the type is not changed. */ void setType(int typeId); Q_INVOKABLE GraphTheory::NodeWrapper * from() const; Q_INVOKABLE GraphTheory::NodeWrapper * to() const; Q_INVOKABLE bool directed() const; /** reimplemented from QObject **/ virtual bool event(QEvent *e) Q_DECL_OVERRIDE; public Q_SLOTS: void updateDynamicProperties(); Q_SIGNALS: void message(const QString &messageString, Kernel::MessageType type) const; void colorChanged(const QColor &color); void typeChanged(); private: Q_DISABLE_COPY(EdgeWrapper) const EdgePtr m_edge; const DocumentWrapper *m_documentWrapper; }; } Q_DECLARE_METATYPE(GraphTheory::EdgeWrapper*) #endif diff --git a/libgraphtheory/kernel/modules/console/consolemodule.h b/libgraphtheory/kernel/modules/console/consolemodule.h index 9d3ff339..5d02d053 100644 --- a/libgraphtheory/kernel/modules/console/consolemodule.h +++ b/libgraphtheory/kernel/modules/console/consolemodule.h @@ -1,82 +1,82 @@ /* * Copyright 2013-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 . */ #ifndef CONSOLEMODULE_H #define CONSOLEMODULE_H #include "graphtheory_export.h" #include "kernel/kernel.h" #include #include #include namespace GraphTheory { /** * \class ConsoleModule - * This class provides an interface object for the scripting engine to write informatoin to + * This class provides an interface object for the scripting engine to write information to * the application console. The application widget that displays these information shall listen * to this object. */ class GRAPHTHEORY_EXPORT ConsoleModule : public QObject { Q_OBJECT public: explicit ConsoleModule(QObject *parent = 0); ~ConsoleModule(); /** * Clear the backlog. */ void clear(); /** * Get all backlog since last clear. * \return the backlog */ QList< QPair > backlog() const; public Q_SLOTS: /** * Print a log message \p message. */ Q_INVOKABLE void log(const QString &message); /** * Print a debug message \p message. */ Q_INVOKABLE void debug(const QString &message); /** * Print an error message \p message. */ Q_INVOKABLE void error(const QString &message); Q_SIGNALS: void message(const QString &message, GraphTheory::Kernel::MessageType type); private: Q_DISABLE_COPY(ConsoleModule) QList< QPair > m_backlog; }; } #endif diff --git a/libgraphtheory/kernel/nodewrapper.cpp b/libgraphtheory/kernel/nodewrapper.cpp index bb83f80a..4a2a17e8 100644 --- a/libgraphtheory/kernel/nodewrapper.cpp +++ b/libgraphtheory/kernel/nodewrapper.cpp @@ -1,455 +1,455 @@ /* * Copyright 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 "nodewrapper.h" #include "edgewrapper.h" #include "graphdocument.h" #include "nodetype.h" #include "edge.h" #include "typenames.h" #include #include #include #include #include using namespace GraphTheory; NodeWrapper::NodeWrapper(NodePtr node, DocumentWrapper *documentWrapper) : QObject(node.data()) , m_node(node) , m_documentWrapper(documentWrapper) { connect(m_node.data(), &Node::idChanged, this, &NodeWrapper::idChanged); connect(m_node.data(), &Node::colorChanged, this, &NodeWrapper::colorChanged); connect(m_node.data(), &Node::positionChanged, this, &NodeWrapper::positionChanged); connect(m_node.data(), &Node::dynamicPropertiesChanged, this, &NodeWrapper::updateDynamicProperties); connect(m_node.data(), &Node::typeChanged, this, &NodeWrapper::typeChanged); updateDynamicProperties(); } NodeWrapper::~NodeWrapper() { } NodePtr NodeWrapper::node() const { return m_node; } int NodeWrapper::id() const { return m_node->id(); } qreal NodeWrapper::x() const { return m_node->x(); } void NodeWrapper::setX(qreal x) { if (x == NodeWrapper::x()) { return; } m_node->setX(x); // change signal will be emitted by connection to m_node signal } qreal NodeWrapper::y() const { return m_node->y(); } void NodeWrapper::setY(qreal y) { if (y == NodeWrapper::y()) { return; } m_node->setY(y); // change signal will be emitted by connection to m_node signal } QString NodeWrapper::color() const { return m_node->color().name(); } void NodeWrapper::setColor(const QString &colorString) { QColor color = QColor(colorString); if (color == m_node->color()) { return; } m_node->setColor(color); // change signal will be emitted by connection to m_node signal } int NodeWrapper::type() const { return m_node->type()->id(); } void NodeWrapper::setType(int typeId) { NodeTypePtr newType = m_node->type(); if (newType->id() == typeId) { return; } for (const auto &type : m_node->document()->nodeTypes()) { if (type->id() == typeId) { newType = type; break; } } if (newType == m_node->type()) { QString command = QString("node.type = %1)").arg(typeId); emit message(i18nc("@info:shell", "%1: node type ID %2 not registered", command, typeId), Kernel::ErrorMessage); return; } m_node->setType(newType); // change signal will be emitted by connection to m_node signal } QList NodeWrapper::edges() const { QList edges; for (const auto &edge : m_node->edges()) { edges.append(m_documentWrapper->edgeWrapper(edge)); } return edges; } QList NodeWrapper::edges(int type) const { EdgeTypePtr typePtr; for (const auto &typeTest : m_node->document()->edgeTypes()) { if (typeTest->id() == type) { typePtr = typeTest; break; } } if (!typePtr) { QString command = QString("node.edges(%1)").arg(type); emit message(i18nc("@info:shell", "%1: edge type ID %2 not registered", command, type), Kernel::ErrorMessage); return QList(); } QList edges; for (const auto &edge : m_node->edges(typePtr)) { edges.append(m_documentWrapper->edgeWrapper(edge)); } return edges; } QList NodeWrapper::inEdges() const { QList edges; for (const auto &edge : m_node->inEdges()) { edges.append(m_documentWrapper->edgeWrapper(edge)); } return edges; } QList NodeWrapper::inEdges(int type) const { EdgeTypePtr typePtr; for (const auto &typeTest : m_node->document()->edgeTypes()) { if (typeTest->id() == type) { typePtr = typeTest; break; } } if (!typePtr) {; QString command = QString("node.inEdges(%1)").arg(type); emit message(i18nc("@info:shell", "%1: edge type ID %2 not registered", command, type), Kernel::ErrorMessage); return QList(); } QList edges; for (const auto &edge : m_node->inEdges(typePtr)) { edges.append(m_documentWrapper->edgeWrapper(edge)); } return edges; } QList NodeWrapper::outEdges() const { QList edges; for (const auto &edge : m_node->outEdges()) { edges.append(m_documentWrapper->edgeWrapper(edge)); } return edges; } QList NodeWrapper::outEdges(int type) const { EdgeTypePtr typePtr; for (const auto &typeTest : m_node->document()->edgeTypes()) { if (typeTest->id() == type) { typePtr = typeTest; break; } } if (!typePtr) { QString command = QString("node.outEdges(%1)").arg(type); emit message(i18nc("@info:shell", "%1: edge type ID %2 not registered", command, type), Kernel::ErrorMessage); return QList(); } QList edges; for (const auto &edge : m_node->outEdges(typePtr)) { edges.append(m_documentWrapper->edgeWrapper(edge)); } return edges; } QList NodeWrapper::neighbors() const { QSet neighbors; for (const auto &edge : m_node->edges()) { if (m_node == edge->from()) { neighbors.insert(m_documentWrapper->nodeWrapper(edge->to())); } else { neighbors.insert(m_documentWrapper->nodeWrapper(edge->from())); } } return neighbors.values(); } QList NodeWrapper::neighbors(int type) const { EdgeTypePtr typePtr; for (const auto &typeTest : m_node->document()->edgeTypes()) { if (typeTest->id() == type) { typePtr = typeTest; break; } } if (!typePtr) { QString command = QString("node.neighbors(%1)").arg(type); emit message(i18nc("@info:shell", "%1: edge type ID %2 not registered", command, type), Kernel::ErrorMessage); return QList(); } QSet neighbors; for (const auto &edge : m_node->edges(typePtr)) { if (m_node == edge->from()) { neighbors.insert(m_documentWrapper->nodeWrapper(edge->to())); } else { neighbors.insert(m_documentWrapper->nodeWrapper(edge->from())); } } return neighbors.values(); } QList NodeWrapper::predecessors() const { QSet precessors; for (const auto &edge : m_node->inEdges()) { if (edge->type()->direction() == EdgeType::Unidirectional) { precessors.insert(m_documentWrapper->nodeWrapper(edge->from())); continue; } else { if (m_node == edge->from()) { precessors.insert(m_documentWrapper->nodeWrapper(edge->to())); } else { precessors.insert(m_documentWrapper->nodeWrapper(edge->from())); } } } return precessors.values(); } QList NodeWrapper::predecessors(int type) const { EdgeTypePtr typePtr; for (const auto &typeTest : m_node->document()->edgeTypes()) { if (typeTest->id() == type) { typePtr = typeTest; break; } } if (!typePtr) { QString command = QString("node.predecessors(%1)").arg(type); emit message(i18nc("@info:shell", "%1: edge type ID %2 not registered", command, type), Kernel::ErrorMessage); return QList(); } QSet precessors; for (const auto &edge : m_node->inEdges(typePtr)) { if (edge->type()->direction() == EdgeType::Unidirectional) { precessors.insert(m_documentWrapper->nodeWrapper(edge->from())); continue; } else { if (m_node == edge->from()) { precessors.insert(m_documentWrapper->nodeWrapper(edge->to())); } else { precessors.insert(m_documentWrapper->nodeWrapper(edge->from())); } } } return precessors.values(); } QList NodeWrapper::successors() const { QSet successors; for (const auto &edge : m_node->outEdges()) { if (edge->type()->direction() == EdgeType::Unidirectional) { successors.insert(m_documentWrapper->nodeWrapper(edge->to())); continue; } else { if (m_node == edge->from()) { successors.insert(m_documentWrapper->nodeWrapper(edge->to())); } else { successors.insert(m_documentWrapper->nodeWrapper(edge->from())); } } } return successors.values(); } QList NodeWrapper::successors(int type) const { EdgeTypePtr typePtr; for (const auto &typeTest : m_node->document()->edgeTypes()) { if (typeTest->id() == type) { typePtr = typeTest; break; } } if (!typePtr) { QString command = QString("node.successors(%1)").arg(type); emit message(i18nc("@info:shell", "%1: edge type ID %2 not registered", command, type), Kernel::ErrorMessage); return QList(); } QSet successors; for (const auto &edge : m_node->outEdges(typePtr)) { if (edge->type()->direction() == EdgeType::Unidirectional) { successors.insert(m_documentWrapper->nodeWrapper(edge->to())); continue; } else { if (m_node == edge->from()) { successors.insert(m_documentWrapper->nodeWrapper(edge->to())); } else { successors.insert(m_documentWrapper->nodeWrapper(edge->from())); } } } return successors.values(); } QScriptValue NodeWrapper::distance(const QString &lengthProperty, QList< NodeWrapper* > targets) { //TODO at a later point in time: - // factor this algorithm out into a self-containted graph algorithm class + // factor this algorithm out into a self-contained graph algorithm class // Implementation of Floyd-Warshall Algorithm: // asymptotic runtime: O(n^3), n = number of nodes // // 1 let D be a |V| × |V| matrix of minimum distances initialized to ∞ (infinity) // 2 foreach(vertex v) // 3 D[v][v] ← 0 // 4 foreach (edge (u,v)) // 5 D[u][v] := w(u,v) // the weight of the edge (u,v) // 6 for (k from 1 to |V|) // 7 for (i from 1 to |V|) // 8 for (j from 1 to |V|) // 9 if (D[i][j] > D[i][k] + D[k][j]) // 10 D[i][j] := D[i][k] + D[k][j] // 11 end if const NodeList nodes = m_node->document()->nodes(); const EdgeList edges = m_node->document()->edges(); const int n = nodes.length(); // create fast access mapping of node IDs to positions QMap map; for (int i = 0; i < n; ++i) { map.insert(nodes.at(i)->id(), i); } // initialize distance matrix D QVector< QVector > D(n, QVector(n)); for (int i = 0; i < n; ++i) { for (int j = 0; j < n; ++j) { if (i == j) { D[i][j] = 0; } else { D[i][j] = std::numeric_limits::max(); } } } // set edges lengths to D for (const auto &edge : edges) { D[map[edge->from()->id()]][map[edge->to()->id()]] = edge->dynamicProperty(lengthProperty).toDouble(); if (edge->type()->direction() == EdgeType::Bidirectional) { D[map[edge->to()->id()]][map[edge->from()->id()]] = edge->dynamicProperty(lengthProperty).toDouble(); } } // computation for (int k = 0; k < n; ++k) { for (int i = 0; i < n; ++i) { for (int j = 0; j < n; ++j) { if (D[i][j] > D[i][k] + D[k][j]) { D[i][j] = D[i][k] + D[k][j]; } } } } // compute return statement QScriptValue array = m_documentWrapper->engine()->newArray(targets.length()); const int from = map[m_node->id()]; for (int i = 0; i < targets.length(); ++i) { const qreal distance = D[from][map[targets.at(i)->id()]]; array.setProperty(i, distance); } return array; } bool NodeWrapper::event(QEvent *e) { if (e->type() == QEvent::DynamicPropertyChange) { QDynamicPropertyChangeEvent *propertyEvent = static_cast(e); QString name = QString::fromUtf8(propertyEvent->propertyName()); QVariant value = property(propertyEvent->propertyName()); // only propagate property to node object if it is registered if (m_node->dynamicProperties().contains(name)) { m_node->setDynamicProperty(name, value); } return true; } return QObject::event(e); } void NodeWrapper::updateDynamicProperties() { for (const auto &property : m_node->dynamicProperties()) { // property value must not be set to QVariant::Invalid, else the properties are not accessible // from the script engine if (m_node->dynamicProperty(property).isValid()) { setProperty(property.toUtf8(), m_node->dynamicProperty(property)); } else { setProperty(property.toUtf8(), QVariant::Int); } } } diff --git a/libgraphtheory/kernel/nodewrapper.h b/libgraphtheory/kernel/nodewrapper.h index 44652280..3ca88ea1 100644 --- a/libgraphtheory/kernel/nodewrapper.h +++ b/libgraphtheory/kernel/nodewrapper.h @@ -1,207 +1,207 @@ /* * Copyright 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 . */ #ifndef NODEWRAPPER_H #define NODEWRAPPER_H #include "graphtheory_export.h" #include "kernel.h" #include "typenames.h" #include "node.h" #include "graphdocument.h" #include "documentwrapper.h" #include #include class QEvent; namespace GraphTheory { class NodeWrapperPrivate; /** * \class NodeWrapper * Wraps NodePtr to be accessible via QtScript. All properties of the node object are available - * as commong QObject properties. + * as common QObject properties. */ class NodeWrapper : public QObject { Q_OBJECT Q_PROPERTY(int id READ id NOTIFY idChanged) Q_PROPERTY(qreal x READ x WRITE setX NOTIFY positionChanged) Q_PROPERTY(qreal y READ y WRITE setY NOTIFY positionChanged) Q_PROPERTY(QString color READ color WRITE setColor NOTIFY colorChanged) Q_PROPERTY(int type READ type WRITE setType NOTIFY typeChanged) public: NodeWrapper(NodePtr node, DocumentWrapper *documentWrapper); virtual ~NodeWrapper(); NodePtr node() const; /** * If the id value is invalid, -1 is returned. * * @return node identifier */ int id() const; /** * @return x-position of node */ qreal x() const; /** * set x-position of node to @c x */ void setX(qreal x); /** * @return y-position of node */ qreal y() const; /** * set y-position of node to @c y */ void setY(qreal y); /** * @return color of node */ QString color() const; /** * set color of node to @c color */ void setColor(const QString &colorString); /** * @return NodeType::id of corresponding node */ int type() const; /** * Set NodeType of corresponding node by specifying its ID by @p typeId. * If @p typeId does not name the ID of any NodeType, the type is not changed. */ void setType(int typeId); /** * @return list of all edges adjacent to this node */ Q_INVOKABLE QList edges() const; /** * Method returns empty list if invalid or non-existing ID \p type is used. * @param type is the ID of an existing EdgeType * @return list of all edges adjacent to this node of type with ID \p type */ Q_INVOKABLE QList edges(int type) const; /** * @return list of all edges that are either bidirectional or unidirectional and point to this node */ Q_INVOKABLE QList inEdges() const; /** * Method returns empty list if invalid or non-existing ID \p type is used. * @param type is the ID of an existing EdgeType * @return list of all edges that are either bidirectional or unidirectional and point to this node * with ID \p type */ Q_INVOKABLE QList inEdges(int type) const; /** * @return list of all edges that are either bidirectional or unidirectional and point from this node */ Q_INVOKABLE QList outEdges() const; /** * Method returns empty list if invalid or non-existing ID \p type is used. * @param type is the ID of an existing EdgeType * @return list of all edges that are either bidirectional or unidirectional and point from this node * with ID \p type */ Q_INVOKABLE QList outEdges(int type) const; /** * @return list of all neighbors connected to this node */ Q_INVOKABLE QList neighbors() const; /** * @param type is the ID of an existing EdgeType * @return list of all neighbors connected to this node via an edge of type \p type */ Q_INVOKABLE QList neighbors(int type) const; /** * @return list of all neighbors connected by an incoming edge */ Q_INVOKABLE QList predecessors() const; /** * @param type is the ID of an existing EdgeType * @return list of all neighbors connected by an incoming edge of type \p type */ Q_INVOKABLE QList predecessors(int type) const; /** * @return list of all neighbors connected by an outgoing edge */ Q_INVOKABLE QList successors() const; /** * @param type is the ID of an existing EdgeType * @return list of all neighbors connected by an outgoing edge of type \p type */ Q_INVOKABLE QList successors(int type) const; /** * @return array of distances to given set of nodes */ Q_INVOKABLE QScriptValue distance(const QString &lengthProperty, QList targets); /** reimplemented from QObject **/ virtual bool event(QEvent *e) Q_DECL_OVERRIDE; public Q_SLOTS: void updateDynamicProperties(); Q_SIGNALS: void message(const QString &messageString, Kernel::MessageType type) const; void idChanged(int id); void positionChanged(const QPointF &position); void colorChanged(const QColor &color); void typeChanged(); private: Q_DISABLE_COPY(NodeWrapper) const NodePtr m_node; const DocumentWrapper *m_documentWrapper; }; } Q_DECLARE_METATYPE(GraphTheory::NodeWrapper*) #endif diff --git a/libgraphtheory/qml/Scene.qml b/libgraphtheory/qml/Scene.qml index bf60cefb..31523cc7 100644 --- a/libgraphtheory/qml/Scene.qml +++ b/libgraphtheory/qml/Scene.qml @@ -1,517 +1,517 @@ /* * Copyright 2014-2015 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 . */ import QtQuick 2.1 import QtQuick.Controls 1.3 import QtQuick.Layouts 1.0 import QtQuick.Dialogs 1.0 import QtQml.StateMachine 1.0 as DSM import org.kde.rocs.graphtheory 1.0 Item { id: root width: 800 height: 600 focus: true // element create/remove actions signal createNode(real x, real y, int typeIndex); signal createEdge(Node from, Node to, int typeIndex); signal deleteNode(Node node); signal deleteEdge(Edge edge); // exec dialog signals signal showNodePropertiesDialog(Node node); signal showEdgePropertiesDialog(Edge edge); Connections { target: selectMoveAction onToggled: { dsmSelectMove.running = selectMoveAction.checked } } Connections { target: addEdgeAction onToggled: { dsmCreateEdge.running = addEdgeAction.checked } } Keys.onPressed: { switch(event.key) { case Qt.Key_Escape: scene.clearSelection(); event.accepted = true; break; case Qt.Key_Delete: scene.deleteSelected() event.accepted = true; break; case Qt.Key_A: //CTRL+A if (event.modifiers & Qt.ControlModifier) { scene.selectAll(); event.accepted = true; } break; default: break; } } ExclusiveGroup { id: editToolButton } ColumnLayout { id: toolbar ToolButton { action: SelectMoveAction { id: selectMoveAction exclusiveGroup: editToolButton checked: true } } ToolButton { action: AddNodeAction { id: addNodeAction exclusiveGroup: editToolButton } } ToolButton { action: AddEdgeAction { id: addEdgeAction exclusiveGroup: editToolButton onCreateEdge: root.createEdge(from, to, edgeTypeSelector.currentIndex) } } ToolButton { action: DeleteAction { id: deleteAction exclusiveGroup: editToolButton } } } ScrollView { id: sceneScrollView width: root.width - toolbar.width - 20 height: root.height anchors { left: toolbar.right leftMargin: 10 } Rectangle { // white background color: "white" width: parent.width height: parent.height } Item { id: scene width: sceneScrollView.width - 30 height: sceneScrollView.height - 20 z: -10 // must lie behind everything else property variant origin: Qt.point(0, 0) // coordinate of global origin (0,0) in scene signal deleteSelected(); signal startMoveSelected(); signal finishMoveSelected(); signal updateSelection(); signal createEdgeUpdateFromNode(); signal createEdgeUpdateToNode(); function clearSelection() { selectionRect.from = Qt.point(0, 0) selectionRect.to = Qt.point(0, 0) updateSelection(); } function setEdgeFromNode() { addEdgeAction.to = null createEdgeUpdateFromNode(); } function selectAll() { selectionRect.from = Qt.point(0, 0) selectionRect.to = Qt.point(width,height) updateSelection(); } MouseArea { id: sceneAction anchors.fill: parent z: -10 // must lie behind everything else property variant lastMouseClicked: Qt.point(0, 0) property variant lastMousePressed: Qt.point(0, 0) property variant lastMouseReleased: Qt.point(0, 0) property variant lastMousePosition: Qt.point(0, 0) property bool nodePressed: false // if true, the current mouse-press event started at a node onClicked: { lastMouseClicked = Qt.point(mouse.x, mouse.y) if (addNodeAction.checked) { mouse.accepted = true createNode(mouse.x + scene.origin.x, mouse.y + scene.origin.y, nodeTypeSelector.currentIndex); return } } onPressed: { lastMousePressed = Qt.point(mouse.x, mouse.y) lastMousePosition = Qt.point(mouse.x, mouse.y) } onPositionChanged: { lastMousePosition = Qt.point(mouse.x, mouse.y) } onReleased: { lastMouseReleased = Qt.point(mouse.x, mouse.y) sceneAction.nodePressed = false } } SelectionRectangle { id: selectionRect visible: false } Line { id: createLineMarker visible: false fromX: sceneAction.lastMousePressed.x fromY: sceneAction.lastMousePressed.y toX: sceneAction.lastMousePosition.x toY: sceneAction.lastMousePosition.y } Repeater { model: edgeModel EdgeItem { id: edgeItem edge: model.dataRole origin: scene.origin z: -1 // edges must be below nodes EdgePropertyItem { anchors.centerIn: parent edge: model.dataRole } MouseArea { anchors.fill: parent propagateComposedEvents: true onPressed: { if (deleteAction.checked) { deleteEdge(edge) mouse.accepted = true } } onDoubleClicked: { showEdgePropertiesDialog(edgeItem.edge); } } } } Repeater { model: nodeModel NodeItem { id: nodeItem node: model.dataRole origin: scene.origin highlighted: addEdgeAction.from == node || addEdgeAction.to == node property bool __modifyingPosition: false property variant __moveStartedPosition: Qt.point(0, 0) Connections { target: selectionRect onChanged: { if (selectMoveAction.checked && selectionRect.contains(x, y)) { highlighted = true } else { highlighted = false } } } Connections { target: scene onUpdateSelection: { if (selectionRect.contains(x, y)) { highlighted = true } else { highlighted = false } } onDeleteSelected: { if (highlighted) { deleteNode(node) } } onStartMoveSelected: { if (!highlighted) { return } __moveStartedPosition.x = node.x __moveStartedPosition.y = node.y node.x = Qt.binding(function() { return __moveStartedPosition.x + sceneAction.lastMousePosition.x - sceneAction.lastMousePressed.x }) node.y = Qt.binding(function() { return __moveStartedPosition.y + sceneAction.lastMousePosition.y - sceneAction.lastMousePressed.y }) } onFinishMoveSelected: { if (!highlighted) { return } node.x = __moveStartedPosition.x + sceneAction.lastMousePosition.x - sceneAction.lastMousePressed.x node.y = __moveStartedPosition.y + sceneAction.lastMousePosition.y - sceneAction.lastMousePressed.y __moveStartedPosition.x = 0 __moveStartedPosition.y = 0 } onCreateEdgeUpdateFromNode: { if (nodeItem.contains(Qt.point(sceneAction.lastMousePressed.x, sceneAction.lastMousePressed.y))) { node.highlighted = true addEdgeAction.from = node } } onCreateEdgeUpdateToNode: { if (nodeItem.contains(Qt.point(sceneAction.lastMouseReleased.x, sceneAction.lastMouseReleased.y))) { node.highlighted = true addEdgeAction.to = node } } } onXChanged: { if (scene.origin != nodeItem.origin || nodeItem.__modifyingPosition ) { // do nothing if item not initialized return; } if (x < 10) { nodeItem.__modifyingPosition = true; var delta = Math.max((10 - x), 10) scene.origin = Qt.point(scene.origin.x - delta, scene.origin.y); scene.width += delta; nodeItem.__modifyingPosition = false; return; } if (x + width + 10 > scene.width) { nodeItem.__modifyingPosition = true; var delta = Math.max(scene.width - (x + width) + 10, 10); scene.width += delta; nodeItem.__modifyingPosition = false; return; } } onYChanged: { if (scene.origin != nodeItem.origin || nodeItem.__modifyingPosition ) { // do nothing if item not initialized return; } if (y < 10) { nodeItem.__modifyingPosition = true; var delta = (10 - y) scene.origin = Qt.point(scene.origin.x, scene.origin.y - delta); scene.height += delta; nodeItem.__modifyingPosition = false; return; } if (y + height + 10 > scene.height) { nodeItem.__modifyingPosition = true; var delta = Math.max(scene.height - (y + height) + 10, 10); scene.height += delta; nodeItem.__modifyingPosition = false; return; } } NodePropertyItem { anchors.centerIn: parent node: model.dataRole } Drag.active: dragArea.drag.active MouseArea { id: dragArea anchors.fill: parent propagateComposedEvents: true drag.target: { // only enable drag when move/select checked selectMoveAction.checked ? parent : undefined } Loader { id: nodeDialogLoader } onDoubleClicked: { showNodePropertiesDialog(nodeItem.node); mouse.accepted = true } onPressed: { // never handle undefined actions signals if (!(selectMoveAction.checked || deleteAction.checked)) { mouse.accepted = false return } if (deleteAction.checked) { deleteNode(nodeItem.node) mouse.accepted = true return } // single-node move action: directly handle it sceneAction.nodePressed = true if (selectMoveAction.checked && !nodeItem.highlighted) { scene.clearSelection() nodeItem.highlighted = true mouse.accepted = true return } // multi-node move action: gets handled by state-machine if (selectMoveAction.checked && nodeItem.highlighted) { mouse.accepted = false return } } onReleased: { sceneAction.nodePressed = false } } } } } } RowLayout { id: extraToolbarCreateNode visible: addNodeAction.checked anchors { top: sceneScrollView.top right:sceneScrollView.right topMargin: 10 rightMargin: 5 } ComboBox { id: nodeTypeSelector model: nodeTypeModel textRole: "titleRole" } } RowLayout { id: extraToolbarCreateEdge visible: addEdgeAction.checked anchors { top: sceneScrollView.top right:sceneScrollView.right topMargin: 10 rightMargin: 5 } ComboBox { id: edgeTypeSelector model: edgeTypeModel textRole: "titleRole" } } // state machine only for select/move DSM.StateMachine { id: dsmSelectMove initialState: smStateIdle running: true DSM.State { id: smStateIdle DSM.SignalTransition { targetState: smStateMoving signal: sceneAction.onPressed guard: sceneAction.nodePressed } DSM.SignalTransition { targetState: smStateSelecting signal: sceneAction.onPressed guard: !sceneAction.nodePressed } DSM.SignalTransition { signal: sceneAction.onClicked onTriggered: { scene.clearSelection } } } DSM.State { id: smStateSelecting DSM.SignalTransition { signal: sceneAction.onPositionChanged onTriggered: { selectionRect.visible = true selectionRect.to = sceneAction.lastMousePosition } } DSM.SignalTransition { targetState: smStateIdle signal: sceneAction.onReleased } onEntered: { scene.clearSelection() selectionRect.from = sceneAction.lastMousePressed } onExited: { selectionRect.visible = false } } DSM.State { id: smStateMoving DSM.SignalTransition { targetState: smStateIdle signal: sceneAction.onReleased } onEntered: { scene.startMoveSelected(); } onExited: { scene.finishMoveSelected() } } } - // state matchine solely for edge creation + // state machine solely for edge creation DSM.StateMachine { id: dsmCreateEdge initialState: ceStateIdle running: false DSM.State { id: ceStateIdle DSM.SignalTransition { targetState: ceStateCreateEdgeTo signal: sceneAction.onPressed } onExited: { addEdgeAction.to = null scene.createEdgeUpdateFromNode() } } DSM.State { id: ceStateCreateEdgeTo DSM.SignalTransition { targetState: ceStateIdle signal: sceneAction.onReleased } onEntered: { createLineMarker.visible = true } onExited: { scene.createEdgeUpdateToNode() addEdgeAction.apply() createLineMarker.visible = false } } } } diff --git a/src/logging.cpp b/src/logging.cpp index a085ee85..b50f23f5 100644 --- a/src/logging.cpp +++ b/src/logging.cpp @@ -1,23 +1,23 @@ /* * Copyright 2015 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 "logging_p.h" -Q_LOGGING_CATEGORY(ROCS_GENERAL, "org.kde.rocs.general", QtWarningMsg) \ No newline at end of file +Q_LOGGING_CATEGORY(ROCS_GENERAL, "org.kde.rocs.general", QtWarningMsg) diff --git a/src/plugins/scriptapi/scriptapimanager.h b/src/plugins/scriptapi/scriptapimanager.h index 65d72565..8280b7f8 100644 --- a/src/plugins/scriptapi/scriptapimanager.h +++ b/src/plugins/scriptapi/scriptapimanager.h @@ -1,113 +1,113 @@ /* * Copyright 2013-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) 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 14 of version 3 of the license. * * 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 . */ #ifndef SCRIPTAPIMANAGER_H #define SCRIPTAPIMANAGER_H #include #include #include class Object; class QUrl; class QDomDocument; class QFile; class QXmlSchema; /** * \class ScriptApiManager * * Load and provide api documentation objects. */ class ScriptApiManager : public QObject { Q_OBJECT public: explicit ScriptApiManager(QObject *parent = 0); /** * This method loads all api documentation files. */ void loadLocalData(); /** * \return list of all loaded language specifications */ QList objectApiList() const; Object * objectApi(int index) const; /** * Generates HTML document with the API documentation for the specified object. * The generated object is cached such that only the first call to this method * is expensive, later calls only return the cached page; * * \param identifier is the identifier of an ObjectDocumentation object * \return HTML content */ QString objectApiDocument(const QString &identifier); /** * Generates HTML document with the an overview over all available API objects. * * \return HTML content */ QString apiOverviewDocument() const; /** - * Load documenation object specification from locally stored XML file. + * Load documentation object specification from locally stored XML file. * * \param path is the local XML file containing the object API specification * \return true if loaded successfully, otherwise false */ bool loadObjectApi(const QUrl &path); signals: void objectApiAdded(); void objectApiAboutToBeAdded(Object*,int); void objectApiRemoved(); void objectApiAboutToBeRemoved(int,int); private: /** * Load XSD file given by its file name (without ".xsd" suffix). The method searches exclusively * the standard install dir for XSD files in subdirectory "schemes/". * * \param schemeName name of the Xml schema without suffix * \return loaded XML Schema */ QXmlSchema loadXmlSchema(const QString &schemeName) const; /** * Load XML file given by \p file that confirms with XML schema \p scheme. * * \param path is the path to the XML file to be loaded * \param scheme is the XML schema describing the DOM * \return the loaded DOM document */ QDomDocument loadDomDocument(const QUrl &path, const QXmlSchema &schema) const; QList m_objectApiList; QHash m_objectApiDocuments; QList m_objectApiCache; // list of all objects available }; #endif diff --git a/src/ui/fileformatdialog.cpp b/src/ui/fileformatdialog.cpp index 20c740da..e434e9e3 100644 --- a/src/ui/fileformatdialog.cpp +++ b/src/ui/fileformatdialog.cpp @@ -1,158 +1,158 @@ /* This file is part of Rocs. Copyright 2010-2011 Tomaz Canabrava Copyright 2010 Wagner Reck Copyright 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 "fileformatdialog.h" #include "libgraphtheory/fileformats/fileformatmanager.h" #include "fileformats/fileformatinterface.h" #include "graphdocument.h" #include #include #include #include #include #include #include #include #include #include using namespace GraphTheory; FileFormatDialog::FileFormatDialog(QObject* parent) : QObject(parent) { } bool FileFormatDialog::exportFile(GraphDocumentPtr document) const { FileFormatManager manager; QStringList nameFilter; QList exportBackends = manager.backends(FileFormatManager::Export); foreach(FileFormatInterface * f, exportBackends) { //TODO fragile code nameFilter << f->extensions(); } QUrl startDirectory = QUrl::fromLocalFile(Settings::lastOpenedDirectory()); QPointer exportDialog = new QFileDialog(qobject_cast< QWidget* >(parent())); exportDialog->setNameFilters(nameFilter); exportDialog->setLabelText(QFileDialog::Accept, i18nc("@action:button", "Export")); if (exportDialog->exec() != QDialog::Accepted) { return false; } if (exportDialog->selectedFiles().isEmpty()) { return false; } // set file ending const QUrl file = QUrl::fromLocalFile(exportDialog->selectedFiles().first()); // test if any file is overwritten if (QFile::exists(exportDialog->selectedFiles().first())) { if (KMessageBox::warningContinueCancel(qobject_cast< QWidget* >(parent()), i18n( "

The file
'%1'
already exists; if you " "do not want to overwrite it, change the file name to " "something else.

", file.toDisplayString()), i18n("File Exists"), KStandardGuiItem::overwrite()) == KMessageBox::Cancel ) { return false; } } // select plugin by extension const QString filter = exportDialog->selectedNameFilter(); // find match for "(*.foo)" QRegularExpressionMatch match; filter.lastIndexOf(QRegularExpression("\\*\\.[a-zA-Z0-9]+"), -1, &match); const QString ext = match.captured(0).right(match.captured(0).length() - 2); FileFormatInterface * filePlugin = manager.backendByExtension(ext); - if (ext == QString() || !filePlugin) { + if (ext.isEmpty() || !filePlugin) { KMessageBox::error(qobject_cast< QWidget* >(parent()), i18n( "

Cannot resolve suffix of file '%1' to an available file backend. " "Aborting export.

", file.toDisplayString())); qCritical() << "Cannot export file, since cannot find plugin for extension: " << file.toLocalFile() << ext; return false; } filePlugin->setFile(file); filePlugin->writeFile(document); if (filePlugin->hasError()) { KMessageBox::error(qobject_cast< QWidget* >(parent()), i18n( "

Error occurred when writing file: '%1'

", filePlugin->errorString())); qCritical() << "Error occurred when writing file: " << filePlugin->errorString(); return false; } Settings::setLastOpenedDirectory(file.path()); return true; } GraphDocumentPtr FileFormatDialog::importFile() { FileFormatManager manager; QString ext; QList importBackends = manager.backends(FileFormatManager::Import); foreach(FileFormatInterface * f, importBackends) { ext.append(f->extensions().join("")); } ext.append(i18n("*|All files")); QPointer dialog = new QFileDialog(qobject_cast< QWidget* >(parent()), i18nc("@title:window", "Import Graph File into Project")); if (!dialog->exec()) { return GraphDocumentPtr(); } qDebug() << "Extensions:" << ext; QString fileName = dialog->selectedFiles().first(); if (fileName.isEmpty()) { return GraphDocumentPtr(); } int index = fileName.lastIndexOf('.'); FileFormatInterface * filePlugin = 0; if (index == -1) { qDebug() << "Cannot open file without extension."; return GraphDocumentPtr(); } qDebug() << fileName.right(fileName.count() - index); filePlugin =manager.backendByExtension(fileName.right(fileName.count() - index)); if (!filePlugin) { qDebug() << "Cannot handle extension " << fileName.right(3); return GraphDocumentPtr(); } filePlugin->setFile(QUrl::fromLocalFile(fileName)); filePlugin->readFile(); if (filePlugin->hasError()) { qDebug() << "Error loading file" << fileName << filePlugin->errorString(); return GraphDocumentPtr(); } else { return filePlugin->graphDocument(); } } diff --git a/src/ui/scriptoutputwidget.h b/src/ui/scriptoutputwidget.h index 4c21ca68..fb0095bf 100644 --- a/src/ui/scriptoutputwidget.h +++ b/src/ui/scriptoutputwidget.h @@ -1,54 +1,54 @@ /* * 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) 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 14 of version 3 of the license. * * 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 . */ #ifndef SCRIPTOUTPUTWIDGET_H #define SCRIPTOUTPUTWIDGET_H #include "ui_scriptoutputwidget.h" #include "libgraphtheory/kernel/kernel.h" #include /** * \class ScriptOutputWidget * * This widget displays output and debug messages from a ConsoleInterface object. - * Add this widget to your UI, add a ConsoleInterface object and registert that object at your + * Add this widget to your UI, add a ConsoleInterface object and register that object at your * script engine. */ class ScriptOutputWidget : public QWidget { Q_OBJECT public: explicit ScriptOutputWidget(QWidget *parent = 0); bool isOutputClearEnabled() const; public Q_SLOTS: void processMessage(const QString &message, GraphTheory::Kernel::MessageType type); void showDebugOutput(bool show = true); void clear(); private Q_SLOTS: void updateFixOutputButton(); private: Ui::ScriptOutputWidget* ui; }; #endif