1
/*
2
* This file is part of the API Extractor project.
3
*
4
* Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
5
*
6
* Contact: PySide team <contact@pyside.org>
7
*
8
* This program is free software; you can redistribute it and/or
9
* modify it under the terms of the GNU General Public License
10
* version 2 as published by the Free Software Foundation.
11
*
12
* This program is distributed in the hope that it will be useful, but
13
* WITHOUT ANY WARRANTY; without even the implied warranty of
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15
* General Public License for more details.
16
*
17
* You should have received a copy of the GNU General Public License
18
* along with this program; if not, write to the Free Software
19
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20
* 02110-1301 USA
21
*
22
*/
23
24
#include "graph.h"
25
#include <QVector>
26
#include <QDebug>
27
#include <QLinkedList>
28
#include <QSet>
29
#include <iterator>
30
#include <algorithm>
31
#include <iostream>
32
#include <QFile>
33
34
struct Graph::GraphPrivate
35
{
36
    enum Color { WHITE, GRAY, BLACK };
37
    typedef QVector<QSet<int> > Edges;
38
    typedef QSet<int>::const_iterator EdgeIterator;
39
40
    Edges edges;
41
42
    GraphPrivate(int numNodes) : edges(numNodes)
43
    {
44
    }
45
46
    void dfsVisit(int node, QLinkedList<int>& result, QVector<Color>& colors) const
47
    {
48
        colors[node] = GRAY;
49
        EdgeIterator it = edges[node].begin();
50
        for (; it != edges[node].end(); ++it) {
51
            if (colors[*it] == WHITE)
52
                dfsVisit(*it, result, colors);
53
            else if (colors[*it] == GRAY) // This is not a DAG!
54
                return;
55
        }
56
        colors[node] = BLACK;
57
        result.push_front(node);
58
    }
59
};
60
61
Graph::Graph(int numNodes) : m_d(new GraphPrivate(numNodes))
62
{
63
}
64
65
Graph::~Graph()
66
{
67
    delete m_d;
68
}
69
70
int Graph::nodeCount() const
71
{
72
    return m_d->edges.size();
73
}
74
75
QLinkedList<int> Graph::topologicalSort() const
76
{
77
    int nodeCount = Graph::nodeCount();
78
    QLinkedList<int> result;
79
    QVector<GraphPrivate::Color> colors(nodeCount, GraphPrivate::WHITE);
80
81
    for (int i = 0; i < nodeCount; ++i) {
82
        if (colors[i] == GraphPrivate::WHITE)
83
            m_d->dfsVisit(i, result, colors);
84
    }
85
86
    // Not a DAG!
87
    if (result.size() != nodeCount)
88
        return QLinkedList<int>();
89
    return result;
90
}
91
92
bool Graph::containsEdge(int from, int to)
93
{
94
    return m_d->edges[from].contains(to);
95
}
96
97
void Graph::addEdge(int from, int to)
98
{
99
    Q_ASSERT(to < (int)m_d->edges.size());
100
    m_d->edges[from].insert(to);
101
}
102
103
void Graph::removeEdge(int from, int to)
104
{
105
    m_d->edges[from].remove(to);
106
}
107
108
void Graph::dump() const
109
{
110
    for (int i = 0; i < m_d->edges.size(); ++i) {
111
        std::cout << i << " -> ";
112
        std::copy(m_d->edges[i].begin(), m_d->edges[i].end(), std::ostream_iterator<int>(std::cout, " "));
113
        std::cout << std::endl;
114
    }
115
}
116
117
void Graph::dumpDot(const QHash< int, QString >& nodeNames, const QString& fileName) const
118
{
119
    QFile output(fileName);
120
    if (!output.open(QIODevice::WriteOnly))
121
        return;
122
    QTextStream s(&output);
123
    s << "digraph D {\n";
124
    for (int i = 0; i < m_d->edges.size(); ++i) {
125
        GraphPrivate::EdgeIterator it = m_d->edges[i].begin();
126
        for (;it != m_d->edges[i].end(); ++it)
127
            s << '"' << nodeNames[i] << "\" -> \"" << nodeNames[*it] << "\"\n";
128
    }
129
    s << "}\n";
130
}