Added workaround for broken ifstream::putback() on MSVC
[hypercube:hypercube.git] / IO / providers / gml.cpp
1 #include <cstring>
2 #include <cerrno>
3 #include <sstream>
4 #include "gml.h"
5
6 using namespace std;
7
8
9 void GmlGraphInput::error()
10 {
11         if (_token == ERROR)
12                 return;
13
14         ioerr << "GML: parse error on line: " << _line << endl;
15         _token = ERROR;
16 }
17
18 void GmlGraphInput::nextToken()
19 {
20         int c, state = 0;
21         wstring flstr;
22         bool negative = false;
23
24
25         while (1) {
26                 c = _fs.get();
27
28                 switch (state) {
29                         case 0:
30                                 if (c == '#') {
31                                         state = 1;
32                                         break;
33                                 }
34                                 if (isspace(c)) {
35
36                                         break;
37                                 }
38                                 if (c == '[') {
39                                         _token = LBRK;
40                                         return;
41                                 }
42                                 if (c == ']') {
43                                         _token = RBRK;
44                                         return;
45                                 }
46                                 if (isalpha(c)) {
47                                         _string = c;
48                                         state = 2;
49                                         break;
50                                 }
51                                 if (isdigit(c)) {
52                                         _int = c - '0';
53                                         flstr += c;
54                                         state = 3;
55                                         break;
56                                 }
57                                 if (c == '.') {
58                                         _int = 0;
59                                         flstr += c;
60                                         state = 4;
61                                 }
62                                 if (c == '+') {
63                                         _int = 0;
64                                         flstr += c;
65                                         state = 3;
66                                         break;
67                                 }
68                                 if (c == '-') {
69                                         _int = 0;
70                                         negative = true;
71                                         flstr += c;
72                                         state = 3;
73                                         break;
74                                 }
75                                 if (c == '"') {
76                                         _string.clear();
77                                         state = 7;
78                                         break;
79                                 }
80                                 if (c == -1) {
81                                         _token = EOI;
82                                         return;
83                                 }
84                                 error();
85                                 return;
86
87                         case 1:
88                                 if (c == -1) {
89                                         _token = EOI;
90                                         return;
91                                 }
92                                 if (c == '\n') {
93                                         _line++;
94                                         state = 0;
95                                 }
96                                 break;
97
98                         case 2:
99                                 if (isalnum(c)) {
100                                         _string += c;
101                                         break;
102                                 }
103                                 _fs.unget();
104                                 _token = KEY;
105                                 return;
106
107                         case 3:
108                                 if (c == '.') {
109                                         flstr += c;
110                                         state = 4;
111                                         break;
112                                 }
113                                 if (c == 'e' || c == 'E') {
114                                         flstr += c;
115                                         state = 5;
116                                         break;
117                                 }
118                                 if (isdigit(c)) {
119                                         flstr += c;
120                                         _int = _int * 10 + c - '0';
121                                         break;
122                                 }
123                                 _fs.unget();
124                                 if (negative)
125                                         _int = -_int;
126                                 _token = INT;
127                                 return;
128
129                         case 4:
130                                 if (isdigit(c)) {
131                                         _int = _int * 10 + c - '0';
132                                         flstr += c;
133                                         break;
134                                 }
135                                 if (c == 'e' || c == 'E') {
136                                         flstr += c;
137                                         state = 5;
138                                         break;
139                                 }
140                                 _fs.unget();
141                                 wistringstream(flstr) >> _float;
142                                 _token = REAL;
143                                 return;
144
145                         case 5:
146                                 if (c == '+') {
147                                         flstr += c;
148                                         state = 6;
149                                         break;
150                                 }
151                                 if (c == '-') {
152                                         flstr += c;
153                                         state = 6;
154                                         break;
155                                 }
156                                 if (isdigit(c)) {
157                                         flstr += c;
158                                         state = 6;
159                                         break;
160                                 }
161                                 error();
162                                 return;
163
164                         case 6:
165                                 if (isdigit(c)) {
166                                         flstr += c;
167                                         break;
168                                 }
169                                 _fs.unget();
170                                 wistringstream(flstr) >> _float;
171                                 _token = REAL;
172                                 return;
173
174                         case 7:
175                                 if (c == -1) {
176                                         error();
177                                         return;
178                                 }
179                                 if (c == '"') {
180                                         _token = STRING;
181                                         return;
182                                 }
183                                 _string += c;
184                                 break;
185                 }
186         }
187 }
188
189 void GmlGraphInput::compare(Token token)
190 {
191         if (_token == token)
192                 nextToken();
193         else
194                 error();
195 }
196
197 void GmlGraphInput::value(ValueType parent, ValueType key)
198 {
199         switch (_token) {
200                 case INT:
201                         if (parent == NODE && key == ID)
202                                 _nodeAttributes.id = _int;
203                         if (parent == EDGE && key == SOURCE)
204                                 _edgeAttributes.source = _int;
205                         if (parent == EDGE && key == TARGET)
206                                 _edgeAttributes.target = _int;
207                         nextToken();
208                         break;
209                 case REAL:
210                         nextToken();
211                         break;
212                 case STRING:
213                         if (parent == NODE && key == LABEL)
214                                 _nodeAttributes.label = _string;
215                         if (parent == EDGE && key == LABEL)
216                                 _edgeAttributes.label = _string;
217                         nextToken();
218                         break;
219                 case LBRK:
220                         nextToken();
221                         list(key);
222                         compare(RBRK);
223                         break;
224                 default:
225                         error();
226         }
227 }
228
229 void GmlGraphInput::list(ValueType parent)
230 {
231         ValueType type = UNKNOWN;
232
233         while (1) {
234                 switch (_token) {
235                         case KEY:
236                                 type = valueType();
237                                 if (((type == EDGE || type == NODE) && parent != GRAPH)
238                                   || ((type == SOURCE || type == TARGET) && parent != EDGE)) {
239                                         error();
240                                         return;
241                                 }
242
243                                 nextToken();
244                                 value(parent, type);
245
246                                 if (type == NODE) {
247                                         if (_nodeAttributes.id < 0) {
248                                                 error();
249                                                 return;
250                                         }
251                                         Vertex *v = addVertex(_nodeAttributes.id);
252                                         setVertexAttributes(v);
253                                         clearAttributes();
254                                 }
255                                 if (type == EDGE) {
256                                         if (_edgeAttributes.source < 0
257                                           || _edgeAttributes.target < 0) {
258                                                 error();
259                                                 return;
260                                         }
261                                         Edge *e = addEdge();
262                                         setEdgeAttributes(e);
263                                         clearAttributes();
264                                 }
265
266                                 break;
267                         case RBRK:
268                         case EOI:
269                                 return;
270                         default:
271                                 error();
272                                 return;
273                 }
274         }
275 }
276
277 bool GmlGraphInput::parse()
278 {
279         _line = 1;
280         _token = START;
281         clearAttributes();
282
283         nextToken();
284         list(UNKNOWN);
285
286         _vertexes.clear();
287         clearAttributes();
288
289         if (_token == EOI)
290                 return true;
291         else {
292                 error();
293                 return false;
294         }
295 }
296
297 GmlGraphInput::ValueType GmlGraphInput::valueType()
298 {
299         if (_string == L"graph")
300                 return GRAPH;
301         if (_string == L"node")
302                 return NODE;
303         if (_string == L"edge")
304                 return EDGE;
305         if (_string == L"id")
306                 return ID;
307         if (_string == L"label")
308                 return LABEL;
309         if (_string == L"source")
310                 return SOURCE;
311         if (_string == L"target")
312                 return TARGET;
313
314         return UNKNOWN;
315 }
316
317 Vertex* GmlGraphInput::addVertex(int id)
318 {
319         Vertex *v;
320         map<int, Vertex*>::const_iterator it;
321
322         it = _vertexes.find(id);
323         if (it != _vertexes.end())
324                 return it->second;
325
326         v = _graph->addVertex();
327
328         _vertexes.insert(pair<int, Vertex*>(id, v));
329
330         return v;
331 }
332
333 Edge* GmlGraphInput::addEdge()
334 {
335         Vertex *src, *dst;
336
337         src = addVertex(_edgeAttributes.source);
338         dst = addVertex(_edgeAttributes.target);
339
340         return _graph->addEdge(src, dst);
341 }
342
343 void GmlGraphInput::clearAttributes()
344 {
345         _nodeAttributes.label.clear();
346         _nodeAttributes.id = -1;
347
348         _edgeAttributes.label.clear();
349         _edgeAttributes.source = -1;
350         _edgeAttributes.target = -1;
351 }
352
353 void GmlGraphInput::setVertexAttributes(Vertex *vertex)
354 {
355         if (_nodeAttributes.label.empty()) {
356                 wostringstream ss;
357                 ss << _nodeAttributes.id;
358                 vertex->setText(ss.str());
359         } else
360                 vertex->setText(_nodeAttributes.label);
361 }
362
363 void GmlGraphInput::setEdgeAttributes(Edge *edge)
364 {
365         edge->setText(_edgeAttributes.label);
366 }
367
368
369 IO::Error GmlGraphInput::readGraph(Graph *graph, const char *fileName,
370   Encoding *encoding)
371 {
372         IO::Error err = Ok;
373
374         _graph = graph;
375
376         if (encoding) {
377                 locale lc(locale(), encoding->cvt());
378                 _fs.imbue(lc);
379         }
380
381         _fs.open(fileName);
382         if (!_fs) {
383                 ioerr << fileName << ": " << strerror(errno) << endl;
384                 err = OpenError;
385         } else {
386                 if (!parse())
387                         err = (_fs.fail()) ? ReadError : FormatError;
388         }
389
390         _fs.close();
391         _fs.clear();
392
393         if (err)
394                 _graph->clear();
395
396         return err;
397 }