diff --git a/RocsCore/DataStructure.h b/RocsCore/DataStructure.h index 07616490..ccefaa7c 100644 --- a/RocsCore/DataStructure.h +++ b/RocsCore/DataStructure.h @@ -1,322 +1,323 @@ /* This file is part of Rocs. Copyright 2004-2011 Tomaz Canabrava Copyright 2010-2011 Wagner Reck Copyright 2011-2012 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) any later version. 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 DATASTRUCTURE_H #define DATASTRUCTURE_H #include #include #include #include #include #include "RocsCoreExport.h" #include "CoreTypes.h" #include "Document.h" class Group; class Document; class Data; class Pointer; class DataStructurePrivate; /** * \class DataStructure */ class ROCSLIB_EXPORT DataStructure : public QObject { Q_OBJECT Q_PROPERTY(QString name READ name WRITE setName) // interal deleter class class deleter; friend class deleter; public: static DataStructurePtr create(Document *parent = 0); static DataStructurePtr create(DataStructurePtr other, Document *parent = 0); virtual DataStructurePtr getDataStructure() const; Document *document() const; void setReadOnly(bool r); bool readOnly() const; QScriptValue scriptValue() const; virtual void setEngine(QScriptEngine *engine); QScriptEngine *engine() const; /** * Updates registration of data in internal reference list. * \param data that is set with new type */ void updateData(DataPtr data); /** * Updates registration of pointer in internal reference list. * \param pointer that is set with new type */ void updatePointer(PointerPtr pointer); /** * Returns true if data elements of specified data type are visible, otherwise false. * \param dataType for that visibility information are returned */ bool isDataVisible(int dataType) const; /** * Returns true if pointers of specified pointer type are visible, otherwise false. * \param pointerType for that visibility information are returned */ bool isPointerVisible(int pointerType) const; QString name() const; /** * Gives list of data elements of specified type if type exists. * If dataType does not exists, returns empty list. * \param dataType is type of data elements to be returned * \return DataList of data elements of specified type */ const DataList dataList(int dataType) const; /** * Gives list of all data elements of all existing types. * \return DataList of data elements */ DataList dataListAll() const; /** * Gives list of pointers of specified type if type exists. * If pointerType does not exists, returns empty list. * \param pointerType is type of pointers to be returned * \return PointerList of pointers of specified type */ const PointerList pointers(int pointerType) const; /** * Gives list all pointers of all existing types. * \return PointerList of pointers */ PointerList pointerListAll() const; const QList groups() const; /** @brief clear data that only is useful for a type of data structure and that cannot be converted to others */ virtual void cleanUpBeforeConvert(); /** * Gives a map with plugin specific properties of the data structure. * \return map keys are property names, values are property values. */ virtual QMap pluginProperties() const; /** * Set plugin specific properties of data structure. * \param identifier is the unique identifier for this property * \param value is the to be set value for the property */ virtual void setPluginProperty(const QString& /*identifier*/, const QString& /*property*/); public Q_SLOTS: virtual DataPtr addData(const QString& name, int dataType); /** * \deprecated * This method only calls \ref addData for each element */ virtual DataList addDataList(DataList dataList, int dataType); /** * Creates new pointer from data element "from" to data element "to" of * given type "pointerType". The pointer type must exist. * * \param from data element where the pointer starts * \param to data element where the pointer ends * \param pointerType is the type of this pointer */ virtual PointerPtr addPointer(DataPtr from, DataPtr to, int pointerType); /** * Access data element by its unique identifier. * Operation has amortized access time of O(1) (worst case O(n)). * * \param uniqueIdentifier the unique identifier of the data element */ DataPtr getData(int uniqueIdentifier); /** * Remove \p data from data structure and (if necessary) destroys the data object. * It is valid to call this method more than once for the same data object. * * \param data the data element that shall be removed */ virtual void remove(DataPtr data); /** * Remove \p pointer from data structure and (if necessary) destroys the pointer object. * It is valid to call this method more than once for the same pointer object. * * \param pointer the pointer that shall be removed */ virtual void remove(PointerPtr pointer); /** * Remove \p group from data structure and (if necessary) destroys the group object. * It is valid to call this method more than once for the same group object. * * \param group the group that shall be removed */ virtual void remove(GroupPtr group); virtual GroupPtr addGroup(const QString& name); virtual DataList addDataList(QList< QPair > dataList, int dataType); virtual DataPtr addData(const QString& name, const QPointF& point, int dataType); void addDynamicProperty(const QString& property, const QVariant& value = QVariant(0)); void removeDynamicProperty(const QString& property); void renameDynamicProperty(const QString& oldName, const QString& newName); /** add the property named \p name to this Data structure. * \param name is an identifier to property. * \param value is the initial value of property. */ void add_property(const QString& name, const QVariant& value); /** remove the string named \p name from this data structure. * \param name is the name of property to be removed. */ void remove_property(const QString& name); /** * if this datastructure shall be deleted, call ONLY this function */ void remove(); // setters void setName(const QString& s); private Q_SLOTS: /** * Only for internal use of the data structure: Slot that is connected signal * Document::dataTypeCreated(...). this method registers data type for this data structure. * \param identifier is the data type identifier of data type that will be registered */ void registerDataType(int identifier); /** * Only for internal use of the data structure: Slot that is connected signal * Document::pointerTypeCreated(...). this method registers pointer type for this data structure. * \param identifier is the pointer type identifier of pointer type that will be registered */ void registerPointerType(int identifier); /** * Only for internal use of the data structure: Slot that is connected signal * Document::dataTypeRemoved(...). this method removes data type for this data structure. * \param identifier is the data type identifier of data type that will be removed */ void removeDataType(int identifier); /** * Only for internal use of the data structure: Slot that is connected signal * Document::pointerTypeRemoved(...). this method removes pointer type for this data structure. * \param identifier is the pointer type identifier of pointer type that will be removed */ void removePointerType(int identifier); Q_SIGNALS: void dataCreated(DataPtr n); void dataPositionChanged(const QPointF); void pointerCreated(PointerPtr e); - void nameChanged(const QString& name); + void nameChanged(const QString &name); void changed(); + void scriptError(const QString &message); protected: DataPtr addData(DataPtr data, int dataType); PointerPtr addPointer(PointerPtr pointer, int pointerType); int generateUniqueIdentifier(); /** * Default constructor. * * \param parent the parent document of the data structure */ DataStructure(Document *parent = 0); /** * Default destructor. */ virtual ~DataStructure(); /** * overwrites the current DataStructure with all values (Data and Pointer) * from the given datastructure object. * * \param other the data structure that shall be imported * \return void */ virtual void importStructure(DataStructurePtr other); void initialize(); template static DataStructurePtr create(Document *parent = 0) { DataStructurePtr pi(new T(parent), deleter()); pi->setQpointer(pi); pi->initialize(); return pi; } template static DataStructurePtr create(DataStructurePtr other, Document *parent = 0) { DataStructurePtr pi(new T(parent), deleter()); pi->setQpointer(pi); pi->initialize(); pi->importStructure(other); return pi; } private: /** * \internal * d-Pointer. */ const QScopedPointer d; DataStructure(const DataStructure&); DataStructure& operator=(const DataStructure&); /** * \internal * Set q-pointer in private data object. */ void setQpointer(DataStructurePtr q); /** * \internal * Private deleter class. */ class deleter { public: void operator()(DataStructure * p) { delete p; } }; }; #endif diff --git a/RocsCore/DataStructures/Graph/GraphStructure.cpp b/RocsCore/DataStructures/Graph/GraphStructure.cpp index 2b1ccf51..6dc94f01 100644 --- a/RocsCore/DataStructures/Graph/GraphStructure.cpp +++ b/RocsCore/DataStructures/Graph/GraphStructure.cpp @@ -1,470 +1,469 @@ /* This file is part of Rocs. Copyright 2011 Tomaz Canabrava Copyright 2011 Wagner Reck Copyright 2011-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 . */ #undef QT_STRICT_ITERATORS // boost property map can't work with iterators being classes #include "GraphStructure.h" #include "KDebug" #include "Data.h" #include "Pointer.h" #include "Document.h" #include "DataStructure.h" #include #include "GraphNode.h" #include "QtScriptBackend.h" #include #include #include #include #include #include #include DataStructurePtr Rocs::GraphStructure::create(Document *parent) { return DataStructure::create(parent); } DataStructurePtr Rocs::GraphStructure::create(DataStructurePtr other, Document *parent) { boost::shared_ptr ds = boost::static_pointer_cast(Rocs::GraphStructure::create(parent)); ds->importStructure(other); return ds; } Rocs::GraphStructure::GraphStructure(Document* parent) : DataStructure(parent) { _type = Graph; } void Rocs::GraphStructure::importStructure(DataStructurePtr other) { //FIXME this import does not correctly import different types setGraphType(Graph); QHash dataTodata; //FIXME only default data type considered foreach(DataPtr n, other->dataList(0)) { DataPtr newdata = addData("", 0); //n->name()); newdata->setColor(n->color()); newdata->setProperty("value", n->property("value").toString()); newdata->setX(n->x()); newdata->setY(n->y()); newdata->setWidth(n->width()); dataTodata.insert(n.get(), newdata); } //FIXME only default pointer type considered foreach(PointerPtr e, other->pointers(0)) { DataPtr from = dataTodata.value(e->from().get()); DataPtr to = dataTodata.value(e->to().get()); PointerPtr newPointer = addPointer(from, to, 0); if (newPointer.get()){ newPointer->setColor(e->color()); newPointer->setProperty("value", e->property("value").toString()); } } } Rocs::GraphStructure::~GraphStructure() { } QScriptValue Rocs::GraphStructure::nodes() { QScriptValue array = engine()->newArray(); foreach(int type, document()->dataTypeList()) { foreach(DataPtr n, dataList(type)) { array.property("push").call(array, QScriptValueList() << n->scriptValue()); } } return array; } QScriptValue Rocs::GraphStructure::nodes(int type) { QScriptValue array = engine()->newArray(); foreach(DataPtr n, dataList(type)) { array.property("push").call(array, QScriptValueList() << n->scriptValue()); } return array; } QScriptValue Rocs::GraphStructure::edges() { QScriptValue array = engine()->newArray(); foreach(int type, document()->pointerTypeList()) { foreach(PointerPtr n, pointers(type)) { array.property("push").call(array, QScriptValueList() << n->scriptValue()); } } return array; } QScriptValue Rocs::GraphStructure::edges(int type) { QScriptValue array = engine()->newArray(); foreach(PointerPtr n, pointers(type)) { array.property("push").call(array, QScriptValueList() << n->scriptValue()); } return array; } QScriptValue Rocs::GraphStructure::createNode() { return createNode(0); } QScriptValue Rocs::GraphStructure::createNode(int type) { DataPtr n = addData("", type); n->setEngine(engine()); return n->scriptValue(); } QScriptValue Rocs::GraphStructure::createEdge(Data* fromRaw, Data* toRaw) { return createEdge(fromRaw, toRaw, 0); } QScriptValue Rocs::GraphStructure::createEdge(Data* fromRaw, Data* toRaw, int type) { if (fromRaw == 0 || toRaw == 0) { kError() << "No edge added: data does not exist"; emit scriptError(i18n("Cannot create edge: nodes are not defined.")); return QScriptValue(); } if (!document()->pointerTypeList().contains(type)) { emit scriptError(i18n("Cannot create edge: pointer type %1 not defined", type)); return QScriptValue(); } DataPtr from = fromRaw->getData(); DataPtr to = toRaw->getData(); PointerPtr edge = addPointer(from, to, type); if (edge) { edge->setEngine(engine()); return edge->scriptValue(); } kError() << "Could not add pointer to data structure"; return QScriptValue(); } QScriptValue Rocs::GraphStructure::overlay_edges(int overlay) { emit scriptError(i18n("The global method \"%1\" is deprecated, please use \"%2\" instead.", QString("overlay_edges(int type)"), QString("edges(int type)"))); return edges(overlay); } QScriptValue Rocs::GraphStructure::list_nodes() { emit scriptError(i18n("The global method \"%1\" is deprecated, please use \"%2\" instead.", QString("list_nodes()"), QString("nodes()"))); return nodes(); } QScriptValue Rocs::GraphStructure::list_nodes(int type) { emit scriptError(i18n("The global method \"%1\" is deprecated, please use \"%2\" instead.", QString("list_nodes(int type)"), QString("nodes(int type)"))); return nodes(type); } QScriptValue Rocs::GraphStructure::list_edges() { emit scriptError(i18n("The global method \"%1\" is deprecated, please use \"%2\" instead.", QString("list_edges()"), QString("edges()"))); return edges(); } QScriptValue Rocs::GraphStructure::list_edges(int type) { emit scriptError(i18n("The global method \"%1\" is deprecated, please use \"%2\" instead.", QString("list_edges(int type)"), QString("edges(int type)"))); return edges(type); } QScriptValue Rocs::GraphStructure::add_node(const QString& name) { emit scriptError(i18n("The global method \"%1\" is deprecated, please use \"%2\" instead.", QString("add_node(string name)"), QString("createNode()"))); DataPtr n = addData(name, 0); n->setEngine(engine()); return n->scriptValue(); } QScriptValue Rocs::GraphStructure::add_edge(Data* fromRaw, Data* toRaw) { emit scriptError(i18n("The global method \"%1\" is deprecated, please use \"%2\" instead.", QString("add_edge(from, to)"), QString("createEdge(node from, node to)"))); return add_overlay_edge(fromRaw, toRaw, 0); } QScriptValue Rocs::GraphStructure::add_overlay_edge(Data* fromRaw, Data* toRaw, int overlay) { emit scriptError(i18n("The global method \"%1\" is deprecated, please use \"%2\" instead.", QString("add_overlay_edge(from, to, overlay)"), QString("createEdge(node from, node to, int type)"))); return createEdge(fromRaw, toRaw, overlay); } QScriptValue Rocs::GraphStructure::dijkstra_shortest_path(Data* fromRaw, Data* toRaw) { if (fromRaw == 0 || toRaw == 0) { return QScriptValue(); } DataPtr from = fromRaw->getData(); DataPtr to = toRaw->getData(); QMap shortestPaths = dijkstraShortestPaths(from); QScriptValue pathEdges = engine()->newArray(); foreach (PointerPtr edge, shortestPaths[to]) { pathEdges.property("push").call( pathEdges, QScriptValueList() << edge->scriptValue() ); } return pathEdges; } QScriptValue Rocs::GraphStructure::distances(Data* fromRaw) { if (fromRaw == 0) { return QScriptValue(); } DataPtr from = fromRaw->getData(); QMap shortestPaths = dijkstraShortestPaths(from); QScriptValue distances = engine()->newArray(); foreach (DataPtr target, dataListAll()) { qreal length = 0; foreach (PointerPtr edge, shortestPaths[target]) { if (!edge->property("value").toString().isEmpty()) { length += edge->property("value").toDouble(); } else { length += 1; } } distances.property("push").call( distances, QScriptValueList() << length ); } return distances; } QMap Rocs::GraphStructure::dijkstraShortestPaths(DataPtr from) { // use copies of these lists to be safe agains changes of // data/pointer lists while computing shortest paths DataList dataListAll = this->dataListAll(); PointerList pointerListAll = this->pointerListAll(); if (!from) { return QMap(); } typedef boost::adjacency_list < boost::listS, boost::vecS, boost::directedS, boost::no_property, boost::property > graph_t; typedef boost::graph_traits ::vertex_descriptor vertex_descriptor; typedef boost::graph_traits ::edge_descriptor edge_descriptor; typedef std::pair Edge; // create IDs for all nodes QMap node_mapping; QMap, PointerPtr> edge_mapping; // to map all edges back afterwards int counter = 0; foreach(DataPtr data, dataListAll) { node_mapping[data->identifier()] = counter++; } // use doubled size for case of undirected edges QVector edges(pointerListAll.count() * 2); QVector weights(pointerListAll.count() * 2); counter = 0; foreach(PointerPtr p, pointerListAll) { edges[counter] = Edge(node_mapping[p->from()->identifier()], node_mapping[p->to()->identifier()]); edge_mapping[std::make_pair < int, int > (node_mapping[p->from()->identifier()], node_mapping[p->to()->identifier()])] = p; if (!p->property("value").toString().isEmpty()) { weights[counter] = p->property("value").toDouble(); } else { weights[counter] = 1; } counter++; // if graph is directed, also add back-edges if (p->direction() == PointerType::Bidirectional) { edges[counter] = Edge(node_mapping[p->to()->identifier()], node_mapping[p->from()->identifier()]); edge_mapping[std::make_pair< int, int >(node_mapping[p->to()->identifier()], node_mapping[p->from()->identifier()])] = p; if (!p->property("value").toString().isEmpty()) { weights[counter] = p->property("value").toDouble(); } else { weights[counter] = 1; } counter++; } } // setup the graph graph_t g(edges.begin(), edges.end(), weights.begin(), dataListAll.count() ); // compute Dijkstra vertex_descriptor source = boost::vertex(node_mapping[from->identifier()], g); QVector p(boost::num_vertices(g)); QVector dist(boost::num_vertices(g)); boost::dijkstra_shortest_paths(g, source, boost::predecessor_map(p.begin()).distance_map(dist.begin()) ); // walk search tree and setup solution QMap shortestPaths = QMap(); DataList::iterator toIter = dataListAll.begin(); while (toIter != dataListAll.end()){ PointerList path = PointerList(); vertex_descriptor target = boost::vertex(node_mapping[(*toIter)->identifier()], g); vertex_descriptor predecessor = target; do { if (edge_mapping.contains(std::make_pair(p[predecessor], predecessor))) { path.append(edge_mapping[std::make_pair < int, int > (p[predecessor], predecessor)]); } predecessor = p[predecessor]; } while (p[predecessor] != predecessor); shortestPaths.insert((*toIter), path); ++toIter; } return shortestPaths; } void Rocs::GraphStructure::setGraphType(int type) { if (_type == type) { return; } if (_type == Multigraph && type != _type) { if (KMessageBox::warningContinueCancel(0, i18n("This action will probably remove some edges. Do you want to continue?")) != KMessageBox::Continue) { return; } } else { // for switch to multigraph nothing has to be done _type = GRAPH_TYPE(type); return; } // need to convert multigraph to graph //FIXME only default data type considered foreach(DataPtr data, dataList(0)) { // Clear the rest. there should be only one edge between two nodes. foreach(DataPtr neighbor, data->adjacentDataList()) { if (data == neighbor) { continue; } while (data->pointerList(neighbor).count() > 1) { data->pointerList(neighbor).last()->remove(); } } } } Rocs::GraphStructure::GRAPH_TYPE Rocs::GraphStructure::graphType() const { return _type; } bool Rocs::GraphStructure::multigraph() const { return (_type == Multigraph); } PointerPtr Rocs::GraphStructure::addPointer(DataPtr from, DataPtr to, int pointerType) { bool directed = document()->pointerType(pointerType)->direction() == PointerType::Unidirectional; if (!directed && !multigraph()) { // do not add back-edges if graph is undirected foreach(PointerPtr pointer, from->pointerList(to)) { if (pointer->pointerType() == pointerType) { - document()->engineBackend()->debug( - i18n("Could not add back-edge (%1->%2) to undirected graph.", from->identifier(), to->identifier())); + emit scriptError(i18n("Could not add back-edge (%1->%2) to undirected graph.", from->identifier(), to->identifier())); return PointerPtr(); } } } if (!multigraph()) { // do not add double edges PointerList list = from->outPointerList(); foreach(PointerPtr tmp, list) { if (tmp->to() == to && tmp->pointerType() == pointerType) { - document()->engineBackend()->debug( + emit scriptError( i18n("Could not add existing edge (%1->%2): this graph is no multigraph.", from->identifier(), to->identifier())); return PointerPtr(); } } } return DataStructure::addPointer(from, to, pointerType); } DataPtr Rocs::GraphStructure::addData(const QString& name, int dataType) { if (readOnly()) { return DataPtr(); } boost::shared_ptr n = boost::static_pointer_cast( GraphNode::create(getDataStructure(), generateUniqueIdentifier(), dataType) ); n->setProperty("name", name); return addData(n, dataType); } QMap Rocs::GraphStructure::pluginProperties() const { QMap properties = QMap(); properties.insert("type", QString("%1").arg(_type)); return properties; } void Rocs::GraphStructure::setPluginProperty(const QString& identifier, const QString& property) { if (identifier.startsWith(QLatin1String("type"))) { setGraphType(property.toInt()); } else { kDebug() << "Skipping unknown graph structure property: " << identifier << " / " << property; } } diff --git a/RocsCore/DataStructures/Graph/GraphStructure.h b/RocsCore/DataStructures/Graph/GraphStructure.h index d97815d4..e316aaa2 100644 --- a/RocsCore/DataStructures/Graph/GraphStructure.h +++ b/RocsCore/DataStructures/Graph/GraphStructure.h @@ -1,272 +1,270 @@ /* This file is part of Rocs. Copyright 2011 Tomaz Canabrava Copyright 2011 Wagner Reck Copyright 2011-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 GRAPHSTRUCTURE_H #define GRAPHSTRUCTURE_H #include "rocs_graphstructure_export.h" #include "DataStructure.h" namespace Rocs { class ROCS_GRAPHSTRUCTURE_EXPORT GraphStructure : public DataStructure { Q_OBJECT Q_PROPERTY(QString name READ name WRITE setName) public: typedef enum { Graph, Multigraph } GRAPH_TYPE; //to avoid hide some methods using DataStructure::remove; using DataStructure::addPointer; using DataStructure::addData; static DataStructurePtr create(Document *parent); static DataStructurePtr create(DataStructurePtr other, Document *parent); explicit GraphStructure(Document* parent = 0); ~GraphStructure(); void importStructure(DataStructurePtr other); /** * Internal method to create new graph edge. * Use \see Pointer::create(...) for creating new edges. * \param from is the origin of the new edge * \param to is the target of the new edge * \param pointerType is the type of this edge, defaults to 0 * \return the created edge as PointerPtr */ PointerPtr addPointer(DataPtr from, DataPtr to, int pointerType); /** * Internal method to create new graph node. * Use \see Data::create(...) for creating new nodes. * \param name is the name of the node * \param dataType is the type of this node, defaults to 0 * \return the created node as DataPtr */ DataPtr addData(const QString& name, int dataType); /** * Returns type of the graph given by enum \see GRAPH_TYPE. * \return graph type */ GRAPH_TYPE graphType() const; bool multigraph() const; /** * Gives a map with plugin specific properties of the data structure. * Implementation of virtual method \see DataStructure::pluginProperties() * \return map keys are property names, values are property values. */ QMap pluginProperties() const; /** * Set plugin specific properties of data structure. * \param identifier is the unique identifier for this property * \param value is the to be set value for the property */ void setPluginProperty(const QString& identifier, const QString& property); /** * Computes the Dijkstra's shortest path algorithm to compute * all shortest path distances from \p from. Note: this shortest path * algorithm works only for graphs with all edges values non-negative. * For undirected graphs reverse edges are add automatically. * The algorithm has time complexity O(V log V + E). * * \param from the node from which the computation starts * \return list is map with keys = target elements, values = path to target */ QMap dijkstraShortestPaths(DataPtr from); /** * Returns a list of all nodes of the graph. * \return array containing the nodes */ Q_INVOKABLE QScriptValue nodes(); /** * Returns a list of all nodes of given \p type in the graph. * \param type is the overlay for the to created pointer * \return array containing the nodes */ Q_INVOKABLE QScriptValue nodes(int type); /** * Returns a list of all edges of the graph. * \return array containing the edges */ Q_INVOKABLE QScriptValue edges(); /** * Returns a list of all edges of given \p type in the graph. * \param type is the overlay for the to created pointer * \return array containing the edges */ Q_INVOKABLE QScriptValue edges(int type); /** * Creates a new data element and return it. If the specified data type is not registered, * no data element is created. * \param type is the data type of the created node * \return script value for the new node */ Q_INVOKABLE QScriptValue createNode(int type); /** * Creates a new data element and return it. * \return script value for the new node */ Q_INVOKABLE QScriptValue createNode(); /** * Creates a new edge edge from \param fromRaw to \param toRaw * of pointer type \param type. If the specified pointer type does not exist, no pointer * is created. * \param fromRaw is origin of pointer * \param toRaw is target of pointer * \param overlay is the overlay for the to created pointer * \return script value for the new pointer */ Q_INVOKABLE QScriptValue createEdge(Data* fromRaw, Data* toRaw, int type); /** * Creates a new edge from \param fromRaw to \param toRaw. * \param fromRaw is origin of pointer * \param toRaw is target of pointer * \return script value for the new pointer */ Q_INVOKABLE QScriptValue createEdge(Data* fromRaw, Data* toRaw); -signals: - void scriptError(const QString &message); public slots: /** * Setter for graph type. No conversations are performed. * \param type as specified by enum GRAPH_TYPE * \return void */ void setGraphType(int type); /** * Returns a list of all nodes of the graph. * \return array containing the nodes * \deprecated */ QScriptValue list_nodes(); /** * Returns a list of all nodes of given \p type in the graph. * \param type is the overlay for the to created pointer * \return array containing the nodes * \deprecated */ QScriptValue list_nodes(int type); /** * Returns a list of all edges of the graph. * \return array containing the edges * \deprecated */ QScriptValue list_edges(); /** * Returns a list of all edges of given \p type in the graph. * \param type is the overlay for the to created pointer * \return array containing the edges * \deprecated */ QScriptValue list_edges(int type); /** * Gives array of edges of specified overlay. If overlay * does not exist an empty array is returned. * \param overlay integer that identifies the overlay * \return QScriptValue array * \deprecated */ QScriptValue overlay_edges(int overlay); /** * Creates a new node with specified \param name. * \param name of the new node * \return script value for the new node * \deprecated */ QScriptValue add_node(const QString& name); /** * Creates a new overlay edge from \param fromRaw to \param toRaw * at overlay \param overlay. If the overlay does not exist no pointer * is created. * \param fromRaw is origin of pointer * \param toRaw is target of pointer * \param overlay is the overlay for the to created pointer * \return script value for the new pointer * \deprecated */ QScriptValue add_overlay_edge(Data* fromRaw, Data* toRaw, int overlay); /** * Creates a new edge from \param fromRaw to \param toRaw. * \param fromRaw is origin of pointer * \param toRaw is target of pointer * \return script value for the new pointer * \deprecated */ QScriptValue add_edge(Data* fromRaw, Data* toRaw); /** * Computes the Dijkstra's shortest path algorithm to compute * the shortes path from \param from to \param to. Note: this shortest path * algorithm works only for graphs with all edges values non-negative. * For undirected graphs reverse edges are add automatically. * The algorithm has time complexity O(V log V + E). * * \param from the node from which the computation starts * \param to the node to which the shortest path shall be computed * \return the edge set of the shortest path */ QScriptValue dijkstra_shortest_path(Data* fromRaw, Data* toRaw); /** * Computes the Dijkstra's shortest path algorithm to compute * all shortest path distances from \p from. If edge has value 0, the edge value * is set to 1. Note: this shortest path algorithm works only for graphs with all * edges values non-negative. For undirected graphs reverse edges are add automatically. * The algorithm has time complexity O(V log V + E). * * \param from the node from which the computation starts * \return the edge set of the shortest path */ QScriptValue distances(Data* fromRaw); private: GraphStructure::GRAPH_TYPE _type; }; } #endif // GRAPHSTRUCTURE_H diff --git a/RocsCore/QtScriptBackend.cpp b/RocsCore/QtScriptBackend.cpp index cddee220..af2afd3b 100644 --- a/RocsCore/QtScriptBackend.cpp +++ b/RocsCore/QtScriptBackend.cpp @@ -1,295 +1,304 @@ /* This file is part of Rocs. Copyright 2004-2011 Tomaz Canabrava Copyright 2012 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) any later version. 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 "QtScriptBackend.h" #include "DataStructure.h" #include "Data.h" #include "Document.h" #include "DocumentManager.h" #include #include #include #include class QtScriptBackendPrivate { public: QString _currentScript; Document *_document; QScriptEngine *_engine; QScriptEngineDebugger *_engineSteps; // used for stepped execution IncludeManager _includeManager; bool _runningTool; QtScriptBackendPrivate() : _engine(new QScriptEngine()) , _engineSteps(0) , _runningTool(false) { } ~QtScriptBackendPrivate() { delete _engine; } void createGraphList() { QScriptValue graphList = _engine->newArray(); _engine->globalObject().setProperty("graphs", graphList); // Add all the graphs on the array as an array, and if it has a name, // also add it for direct access with it's name. int size = _document->dataStructures().size(); for (int i = 0; i < size; i++) { graphList.property("push").call(graphList, QScriptValueList() << _document->dataStructures().at(i)->scriptValue()); } } }; static QScriptValue debug_script(QScriptContext* context, QScriptEngine* /*engine*/) { DocumentManager::self().activeDocument()->engineBackend()->debug(QString("%1").arg(context->argument(0).toString())); return QScriptValue(); } static QScriptValue output_script(QScriptContext *context, QScriptEngine* /*engine*/) { DocumentManager::self().activeDocument()->engineBackend()->output(QString("%1").arg(context->argument(0).toString())); return QScriptValue(); } static QScriptValue interrupt_script(QScriptContext *context, QScriptEngine* /*engine*/) { Q_UNUSED(context); DocumentManager::self().activeDocument()->engineBackend()->interrupt(); return QScriptValue(); } static QScriptValue include_script(QScriptContext *context, QScriptEngine* /*engine*/) { DocumentManager::self().activeDocument()->engineBackend()->include(QString("%1").arg(context->argument(0).toString())); return QScriptValue(); } QtScriptBackend::QtScriptBackend(QObject* parent) : QObject(parent) , d(new QtScriptBackendPrivate) { } QtScriptBackend::~QtScriptBackend() { } QScriptEngine* QtScriptBackend::engine() const { return d->_engine; } void QtScriptBackend::loadFile(const QString& file) { d->_currentScript.clear(); QFile f(file); if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) { kDebug() << "File not found"; return; } while (! f.atEnd()) { QByteArray line = f.readLine(); d->_currentScript += line; } d->_currentScript += '\n'; } IncludeManager& QtScriptBackend::includeManager() const { return d->_includeManager; } void QtScriptBackend::stop() { // check for stepped execution if (d->_engineSteps) { d->_engineSteps->action(QScriptEngineDebugger::ContinueAction)->trigger(); d->_engineSteps->detach(); d->_engineSteps->deleteLater(); d->_engineSteps = 0; } // abort possibly running execution if (d->_engine && d->_engine->isEvaluating()) { d->_engine->abortEvaluation(); } if (d->_engine) { d->_engine->deleteLater(); d->_engine = 0; } emit finished(); } QString QtScriptBackend::execute() { if (!d->_engine) { d->_engine = new QScriptEngine(this); emit engineCreated(d->_engine); } if (d->_engine->isEvaluating()) { d->_engine->abortEvaluation(); } d->_engine->collectGarbage(); d->_engine->pushContext(); d->_engine->globalObject().setProperty("debug", engine()->newFunction(debug_script)); d->_engine->globalObject().setProperty("output", engine()->newFunction(output_script)); d->_engine->globalObject().setProperty("interrupt", engine()->newFunction(interrupt_script)); d->_engine->globalObject().setProperty("include", engine()->newFunction(include_script)); int size = d->_document->dataStructures().size(); for (int i = 0; i < size; i++) { d->_document->dataStructures().at(i)->setEngine(d->_engine); + connect(d->_document->dataStructures().at(i).get(), SIGNAL(scriptError(QString)), this, SIGNAL(scriptError(QString))); } d->createGraphList(); d->_engine->setProcessEventsInterval(100); //! TODO: Make that changeable. QString result = d->_engine->evaluate(d->_currentScript, i18n("Rocs Console script")).toString(); if (d->_engine && d->_engine->hasUncaughtException()) { emit scriptError(i18n("Script Error: %1", result)); emit scriptError(d->_engine->uncaughtExceptionBacktrace().join("\n")); } if (d->_engine) { emit scriptInfo(i18nc("@info status message after successful script execution", "Execution Finished")); d->_engine->popContext(); + for (int i = 0; i < size; i++) { + d->_document->dataStructures().at(i).get()->disconnect(this); + } } emit finished(); return result; } void QtScriptBackend::executeStep() { // prepare new engine if execution not running yet if (!d->_engine) { d->_engine = new QScriptEngine(this); emit engineCreated(d->_engine); } // create step-executor if not present if (!d->_engineSteps) { d->_engineSteps = new QScriptEngineDebugger(this); d->_engineSteps->setAutoShowStandardWindow(false); // we do not want to have a debugger window d->_engineSteps->attachTo(d->_engine); } if (!d->_engine->isEvaluating()) { d->_engine->globalObject().setProperty("debug", engine()->newFunction(debug_script)); d->_engine->globalObject().setProperty("output", engine()->newFunction(output_script)); d->_engine->globalObject().setProperty("interrupt", engine()->newFunction(interrupt_script)); int size = d->_document->dataStructures().size(); for (int i = 0; i < size; i++) { d->_document->dataStructures().at(i)->setEngine(d->_engine); + connect(d->_document->dataStructures().at(i).get(), SIGNAL(scriptError(QString)), this, SIGNAL(scriptError(QString))); } d->createGraphList(); d->_engine->setProcessEventsInterval(100); QString error = d->_engine->evaluate(d->_currentScript).toString(); if (d->_engine && d->_engine->hasUncaughtException()) { emit scriptError(i18n("Script Error: %1", error)); } } if (!d->_engine || !d->_engine->isEvaluating()) { scriptInfo(i18nc("@info status message after successful script execution", "Execution Finished")); + int size = d->_document->dataStructures().size(); + for (int i = 0; i < size; i++) { + d->_document->dataStructures().at(i).get()->disconnect(this); + } emit finished(); } } bool QtScriptBackend::isRunning() const { if ((d->_engine) && (d->_engine->isEvaluating())) { return true; } return d->_runningTool; } void QtScriptBackend::setScript(const QString& s, Document *graphs) { d->_currentScript = s; d->_document = graphs; } void QtScriptBackend::debug(const QString& message) { emit scriptError(i18n("The global method \"%1\" is deprecated, please use \"%2\" instead.", QString("debug(message)"), QString("Console.debug(message)"))); emit sendDebug(message); } void QtScriptBackend::output(const QString& message) { emit scriptError(i18n("The global method \"%1\" is deprecated, please use \"%2\" instead.", QString("output(message)"), QString("Console.log(message)"))); emit sendOutput(message); emit sendDebug(message); } void QtScriptBackend::continueExecutionStep() { if (d->_engine && d->_engineSteps && d->_engine->isEvaluating()) { d->_engineSteps->action(QScriptEngineDebugger::ContinueAction)->trigger(); } } void QtScriptBackend::include(const QString& filePath) { //TODO switch to URL usage QString fileName = d->_includeManager.seekFile(filePath); if (d->_includeManager.checkIfWasIncluded(fileName)) { return; } QFile f(fileName); if (f.open(QFile::ReadOnly)) { QString script = d->_includeManager.include(f.readAll(), fileName.section('/', 0, -2), fileName.section('/', -1)); d->_engine->currentContext()->setActivationObject(d->_engine->currentContext()->parentContext()->activationObject()); QString error = d->_engine->evaluate(script, filePath).toString(); if (d->_engine && d->_engine->hasUncaughtException()) { emit scriptError(i18n("Script error in included file %1", filePath)); emit scriptError(d->_engine->uncaughtExceptionBacktrace().join("\n")); } } } void QtScriptBackend::interrupt() { if (!d->_engine->isEvaluating()) { return; } //FIXME workaround for now: two signals needed to trigger interrupt if (d->_engineSteps) d->_engineSteps->action(QScriptEngineDebugger::InterruptAction)->trigger(); if (d->_engineSteps) d->_engineSteps->action(QScriptEngineDebugger::InterruptAction)->trigger(); }