graphml.cpp 13.1 KB
Newer Older
Tiago Peixoto's avatar
Tiago Peixoto committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
// graph-tool -- a general graph modification and manipulation thingy
//
// Copyright (C) 2006  Tiago de Paula Peixoto <tiago@forked.de>
//
// 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, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

// Copyright (C) 2004 The Trustees of Indiana University.
//
// Boost Software License - Version 1.0 - August 17th, 2003
//
// Permission is hereby granted, free of charge, to any person or organization
// obtaining a copy of the software and accompanying documentation covered by
// this license (the "Software") to use, reproduce, display, distribute,
// execute, and transmit the Software, and to prepare derivative works of the
// Software, and to permit third-parties to whom the Software is furnished to
// do so, all subject to the following:
//
// The copyright notices in the Software and this entire statement, including
// the above license grant, this restriction and the following disclaimer,
// must be included in all copies of the Software, in whole or in part, and
// all derivative works of the Software, unless such copies or derivative
// works are solely in the form of machine-executable object code generated by
// a source language processor.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

//  Authors: Douglas Gregor
//           Andrew Lumsdaine
//           Tiago de Paula Peixoto

#include "expat.h"
50
#include "graphml.hpp"
Tiago Peixoto's avatar
Tiago Peixoto committed
51

52
using namespace boost;
Tiago Peixoto's avatar
Tiago Peixoto committed
53
54
55
56

class graphml_reader
{
public:
57
    graphml_reader(mutate_graph& g) 
58
        : m_g(g), m_active_descriptor_is_vertex(false), m_canonical_vertices(false), m_canonical_edges(false) { }
Tiago Peixoto's avatar
Tiago Peixoto committed
59
60
61
    
    void run(std::istream& in)
    {
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
        const int buffer_size = 4096;
        XML_Parser parser = XML_ParserCreate(0);
        XML_SetElementHandler(parser, &on_start_element, &on_end_element);
        XML_SetCharacterDataHandler(parser, &on_character_data);
        XML_SetUserData(parser, this);
        char buffer[buffer_size];
        do 
        {
            in.read(buffer, buffer_size);
        } 
        while (XML_Parse(parser, buffer, in.gcount(), in.gcount() == 0) && in.good());

        if (in.good()) 
        {
            std::stringstream s;
            s << "Parse error: " << XML_ErrorString(XML_GetErrorCode(parser))
              << " on line " << XML_GetCurrentLineNumber(parser) 
              <<", column " << XML_GetCurrentColumnNumber(parser);
            throw parse_error(s.str());
        }
        XML_ParserFree(parser);
Tiago Peixoto's avatar
Tiago Peixoto committed
83
84
85
86
87
    }

private:
    /// The kinds of keys. Not all of these are supported
    enum key_kind { 
88
89
90
91
92
93
94
        graph_key, 
        node_key, 
        edge_key,
        hyperedge_key,
        port_key,
        endpoint_key, 
        all_key
Tiago Peixoto's avatar
Tiago Peixoto committed
95
96
97
98
    };

    static void 
    on_start_element(void* user_data, const XML_Char *c_name,
99
                     const XML_Char **atts)
Tiago Peixoto's avatar
Tiago Peixoto committed
100
    {
101
102
103
104
105
106
107
108
109
110
111
112
        graphml_reader* self = static_cast<graphml_reader*>(user_data);

        std::string name(c_name);
        if (name == "key") 
        {
            std::string id;
            std::string key_name;
            std::string key_type;
            key_kind kind = all_key;

            while (*atts) 
            {
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
                std::string name = *atts++;
                std::string value = *atts++;

                if (name == "id") id = value;
                else if (name == "attr.name") key_name = value;
                else if (name == "attr.type") key_type = value;
                else if (name == "for") 
                {
                    if (value == "graph") kind = graph_key;
                    else if (value == "node") kind = node_key;
                    else if (value == "edge") kind = edge_key;
                    else if (value == "hyperedge") kind = hyperedge_key;
                    else if (value == "port") kind = port_key;
                    else if (value == "endpoint") kind = endpoint_key;
                    else if (value == "all") kind = all_key;
                    else 
                    {
                        throw parse_error("unrecognized key kind '" + value + "'");
                    }
                }
133
134
135
136
137
138
139
140
141
142
143
144
145
            }

            self->m_keys[id] = kind;
            self->m_key_name[id] = key_name;
            self->m_key_type[id] = key_type;
            self->m_active_key = id;
        } 
        else if (name == "node") 
        {
            std::string id;

            while (*atts) 
            {
146
147
148
149
                std::string name = *atts++;
                std::string value = *atts++;
                
                if (name == "id") id = value;
150
151
152
153
154
155
156
157
158
159
160
161
            }

            self->handle_vertex(id);
            self->m_active_descriptor = id;
            self->m_active_descriptor_is_vertex = true;
        } 
        else if (name == "edge") 
        {
            std::string id;
            std::string source, target;
            while (*atts) 
            {
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
                std::string name = *atts++;
                std::string value = *atts++;

                if (name == "id") id = value;
                else if (name == "source") source = value;
                else if (name == "target") target = value;
                else if (name == "directed") 
                {
                    bool edge_is_directed = (value == "directed");
                    if (edge_is_directed != self->m_g.is_directed()) 
                    {
                        if (edge_is_directed) 
                            throw directed_graph_error();
                        else
                            throw undirected_graph_error();
                    }
                }
179
180
181
182
183
184
185
186
187
188
            }

            self->handle_edge(id, source, target);
            self->m_active_descriptor = id;
            self->m_active_descriptor_is_vertex = false;
        } 
        else if (name == "graph") 
        {
            while (*atts) 
            {
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
                std::string name = *atts++;
                std::string value = *atts++;
                
                if (name == "edgedefault") 
                {
                    bool edge_is_directed = (value == "directed");
                    if (edge_is_directed != self->m_g.is_directed()) 
                    {
                        if (edge_is_directed) 
                            throw directed_graph_error();
                        else
                            throw undirected_graph_error();
                    }
                }
                else if (name == "parse.nodeids")
                {
                    self->m_canonical_vertices = (value == "canonical");
                }
                else if (name == "parse.edgeids")
                {
                    self->m_canonical_edges = (value == "canonical");
                }
211
212
213
214
215
216
217
            }
            self->m_active_descriptor = "";
        } 
        else if (name == "data") 
        {
            while (*atts) 
            {
218
219
                std::string name = *atts++;
                std::string value = *atts++;
220

221
                if (name == "key") self->m_active_key = value;
222
223
224
225
            }
        }

        self->m_character_data.clear();
Tiago Peixoto's avatar
Tiago Peixoto committed
226
227
228
229
230
    }
    
    static void
    on_end_element(void* user_data, const XML_Char *c_name)
    {
231
232
233
234
        graphml_reader* self = static_cast<graphml_reader*>(user_data);
        std::string name(c_name);

        if (name == "data") 
235
        {            
236
            self->handle_property(self->m_active_key, self->m_active_descriptor,
237
238
                                  self->m_active_descriptor_is_vertex,
                                  self->m_character_data);
239
240
241
242
243
        } 
        else if (name == "default")
        {
            self->m_key_default[self->m_active_key] = self->m_character_data;
        }
Tiago Peixoto's avatar
Tiago Peixoto committed
244
245
246
247
248
    }

    static void
    on_character_data(void* user_data, const XML_Char* s, int len)
    {
249
250
        graphml_reader* self = static_cast<graphml_reader*>(user_data);
        self->m_character_data.append(s, len);
Tiago Peixoto's avatar
Tiago Peixoto committed
251
252
253
254
255
    }

    void 
    handle_vertex(const std::string& v)
    {
256
257
258
259
260
261
262
263
264
        bool is_new = false;

        if (m_canonical_vertices)
        {
            size_t id;

            //strip leading "n" from name
            try 
            {
265
                id = lexical_cast<size_t>(std::string(v,1));
266
267
268
            }
            catch (bad_lexical_cast)
            {
269
                throw parse_error("invalid vertex: " + v);
270
271
272
273
            }
            
            while(id >= m_canonical_vertex.size())
            {
274
275
                m_canonical_vertex.push_back(m_g.do_add_vertex());
                is_new = true;
276
277
278
279
280
281
            }
        }
        else
        {
            if (m_vertex.find(v) == m_vertex.end())
            {
282
283
                m_vertex[v] = m_g.do_add_vertex();
                is_new = true;
284
285
286
287
288
289
290
291
            }
        }

        if (is_new)
        {
            std::map<std::string, std::string>::iterator iter;
            for (iter = m_key_default.begin(); iter != m_key_default.end(); ++iter)
            {
292
293
                if (m_keys[iter->first] == node_key)
                    handle_property(iter->first, v, true, iter->second);
294
295
            }
        }
Tiago Peixoto's avatar
Tiago Peixoto committed
296
297
    }

298
    any
Tiago Peixoto's avatar
Tiago Peixoto committed
299
300
    get_vertex_descriptor(const std::string& v)
    {
301
302
303
304
305
306
307
        if (m_canonical_vertices)
        {
            //strip leading "n" from name
            size_t id = lexical_cast<size_t>(std::string(v,1));
            return m_canonical_vertex[id];
        }
        else
308
        {            
309
310
            return m_vertex[v];
        }
Tiago Peixoto's avatar
Tiago Peixoto committed
311
312
313
314
315
    }

    void 
    handle_edge(const std::string& e, const std::string& u, const std::string& v)
    {
316
317
        handle_vertex(u);
        handle_vertex(v);
Tiago Peixoto's avatar
Tiago Peixoto committed
318

319
320
321
        any source, target;
        source = get_vertex_descriptor(u);
        target = get_vertex_descriptor(v);
Tiago Peixoto's avatar
Tiago Peixoto committed
322

323
324
325
326
327
        any edge;
        bool added;
        tie(edge, added) = m_g.do_add_edge(source, target);
        if (!added)
            throw bad_parallel_edge(u, v);
Tiago Peixoto's avatar
Tiago Peixoto committed
328

329
330
331
        if (m_canonical_edges)
        {
            size_t id;
Tiago Peixoto's avatar
Tiago Peixoto committed
332

333
            //strip leading "e" from name
Tiago Peixoto's avatar
Tiago Peixoto committed
334
            try
335
            {
336
                id = lexical_cast<size_t>(std::string(e,1));
337
338
339
            }
            catch (bad_lexical_cast)
            {
340
                throw parse_error("invalid edge: " + e);
341
342
            }
            if (id != m_canonical_edge.size())
343
                throw parse_error("the following edge is not in order: " + e);
344
345
346
347
348
349
350
351
352
353
354
            m_canonical_edge.push_back(edge);
        }
        else
        {
            m_edge[e] = edge;
        }

        std::map<std::string, std::string>::iterator iter;
        for (iter = m_key_default.begin(); iter != m_key_default.end(); ++iter)
        {
            if (m_keys[iter->first] == edge_key)
355
                handle_property(iter->first, e, false, iter->second);
356
        }
357
    }
Tiago Peixoto's avatar
Tiago Peixoto committed
358
359

    void handle_property(std::string key_id, std::string descriptor, 
360
                         bool is_vertex, std::string value)
Tiago Peixoto's avatar
Tiago Peixoto committed
361
    {
362
363
364
365
366
367
        if (descriptor == "")
            m_g.set_graph_property(m_key_name[key_id], value, m_key_type[key_id]);
        else if (is_vertex)
            m_g.set_vertex_property(m_key_name[key_id], get_vertex_descriptor(descriptor), value, m_key_type[key_id]);
        else
            m_g.set_edge_property(m_key_name[key_id], get_edge_descriptor(descriptor), value, m_key_type[key_id]);
Tiago Peixoto's avatar
Tiago Peixoto committed
368
369
    }

370
    any
Tiago Peixoto's avatar
Tiago Peixoto committed
371
372
    get_edge_descriptor(const std::string& e)
    {
373
374
375
        if (m_canonical_edges)
        {
            //strip leading "e" from name
Tiago Peixoto's avatar
Tiago Peixoto committed
376
            size_t id = lexical_cast<size_t>(std::string(e,1));
377
378
379
380
381
382
            return m_canonical_edge[id];
        }
        else
        {
            return m_edge[e];
        }
Tiago Peixoto's avatar
Tiago Peixoto committed
383
384
    }

385
    mutate_graph& m_g;
Tiago Peixoto's avatar
Tiago Peixoto committed
386
387
388
    std::map<std::string, key_kind> m_keys;
    std::map<std::string, std::string> m_key_name;
    std::map<std::string, std::string> m_key_type;
389
390
391
392
393
    std::map<std::string, std::string> m_key_default;
    std::map<std::string, any> m_vertex;
    std::vector<any> m_canonical_vertex;
    std::map<std::string, any> m_edge;
    std::vector<any> m_canonical_edge;
Tiago Peixoto's avatar
Tiago Peixoto committed
394
395
396
397
398
399
400
401
    std::string m_active_descriptor;
    bool m_active_descriptor_is_vertex;
    std::string m_active_key;
    std::string m_character_data;
    bool m_canonical_vertices;
    bool m_canonical_edges;
};

402
namespace boost
Tiago Peixoto's avatar
Tiago Peixoto committed
403
404
{
void
405
406
407
408
read_graphml(std::istream& in, mutate_graph& g)
{    
    graphml_reader reader(g);
    reader.run(in);
Tiago Peixoto's avatar
Tiago Peixoto committed
409
410
}
}