001/* 002 * Copyright 2006 - 2013 003 * Stefan Balev <stefan.balev@graphstream-project.org> 004 * Julien Baudry <julien.baudry@graphstream-project.org> 005 * Antoine Dutot <antoine.dutot@graphstream-project.org> 006 * Yoann Pigné <yoann.pigne@graphstream-project.org> 007 * Guilhelm Savin <guilhelm.savin@graphstream-project.org> 008 * 009 * This file is part of GraphStream <http://graphstream-project.org>. 010 * 011 * GraphStream is a library whose purpose is to handle static or dynamic 012 * graph, create them from scratch, file or any source and display them. 013 * 014 * This program is free software distributed under the terms of two licenses, the 015 * CeCILL-C license that fits European law, and the GNU Lesser General Public 016 * License. You can use, modify and/ or redistribute the software under the terms 017 * of the CeCILL-C license as circulated by CEA, CNRS and INRIA at the following 018 * URL <http://www.cecill.info> or under the terms of the GNU LGPL as published by 019 * the Free Software Foundation, either version 3 of the License, or (at your 020 * option) any later version. 021 * 022 * This program is distributed in the hope that it will be useful, but WITHOUT ANY 023 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 024 * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 025 * 026 * You should have received a copy of the GNU Lesser General Public License 027 * along with this program. If not, see <http://www.gnu.org/licenses/>. 028 * 029 * The fact that you are presently reading this means that you have had 030 * knowledge of the CeCILL-C and LGPL licenses and that you accept their terms. 031 */ 032package org.graphstream.stream.file; 033 034import java.io.BufferedReader; 035import java.io.FileInputStream; 036import java.io.FileNotFoundException; 037import java.io.IOException; 038import java.io.InputStream; 039import java.io.InputStreamReader; 040import java.io.Reader; 041import java.io.StreamTokenizer; 042import java.net.URL; 043import java.util.ArrayList; 044import java.util.HashMap; 045import java.util.zip.GZIPInputStream; 046 047/** 048 * Class responsible for parsing files in the DGS format (old versions of the 049 * format). 050 * 051 * <p> 052 * The DGS file format is especially designed for storing dynamic graph 053 * definitions into a file. More information about the DGS file format will be 054 * found on the GraphStream web site: <a 055 * href="http://graphstream-project.org/">http://graphstream-project.org/</a> 056 * </p> 057 * 058 * @see OldFileSourceDGS 059 * @see FileSource 060 */ 061public class FileSourceDGS1And2 extends FileSourceBase { 062 // Constants 063 064 /** 065 * Types of attributes. 066 */ 067 protected enum AttributeType { 068 NUMBER, VECTOR, STRING 069 }; 070 071 /** 072 * Pair <name,type> defining an attribute. 073 */ 074 protected static class AttributeFormat { 075 /** 076 * Name of the attribute. 077 */ 078 public String name; 079 080 /** 081 * Type of the attribute. 082 */ 083 public AttributeType type; 084 085 /** 086 * New format descriptor for an attribute. 087 * 088 * @param name 089 * The attribute name. 090 * @param type 091 * The attribute type. 092 */ 093 public AttributeFormat(String name, AttributeType type) { 094 this.name = name; 095 this.type = type; 096 } 097 098 /** 099 * Attribute name. 100 * 101 * @return The name. 102 */ 103 public String getName() { 104 return name; 105 } 106 107 /** 108 * Attribute format. 109 * 110 * @return The format. 111 */ 112 public AttributeType getType() { 113 return type; 114 } 115 } 116 117 // Attributes 118 119 /** 120 * Format version. 121 */ 122 protected int version; 123 124 /** 125 * Name of the graph. 126 */ 127 protected String graphName; 128 129 /** 130 * Number of step given in the header. 131 */ 132 protected int stepCountAnnounced; 133 134 /** 135 * Number of events given in the header. 136 */ 137 protected int eventCountAnnounced; 138 139 /** 140 * Real number of step at current time. 141 */ 142 protected int stepCount; 143 144 /** 145 * Real number of events at current time. 146 */ 147 protected int eventCount; 148 149 /** 150 * Attribute count and type expected for each node add and modify command. 151 */ 152 protected ArrayList<AttributeFormat> nodesFormat = new ArrayList<AttributeFormat>(); 153 154 /** 155 * Attribute count and type expected for each edges add and modify command. 156 */ 157 protected ArrayList<AttributeFormat> edgesFormat = new ArrayList<AttributeFormat>(); 158 159 /** 160 * An attribute set. 161 */ 162 protected HashMap<String, Object> attributes = new HashMap<String, Object>(); 163 164 // Constructors 165 166 /** 167 * New reader for the DGS graph file format versions 1 and 2. 168 */ 169 public FileSourceDGS1And2() { 170 super(true /* EOL is significant */); 171 } 172 173 // Access 174 175 // Command 176 177 @Override 178 public boolean nextEvents() throws IOException { 179 String key = getWordOrSymbolOrStringOrEolOrEof(); 180 String tag = null; 181 182 if (key.equals("ce")) { 183 tag = getStringOrWordOrNumber(); 184 185 readAttributes(edgesFormat); 186 187 for (String k : attributes.keySet()) { 188 Object value = attributes.get(k); 189 sendEdgeAttributeChanged(graphName, tag, k, null, value); 190 } 191 192 if (eatEolOrEof() == StreamTokenizer.TT_EOF) 193 return false; 194 } else if (key.equals("cn")) { 195 tag = getStringOrWordOrNumber(); 196 197 readAttributes(nodesFormat); 198 199 for (String k : attributes.keySet()) { 200 Object value = attributes.get(k); 201 sendNodeAttributeChanged(graphName, tag, k, null, value); 202 } 203 204 if (eatEolOrEof() == StreamTokenizer.TT_EOF) 205 return false; 206 } else if (key.equals("ae")) { 207 tag = getStringOrWordOrNumber(); 208 String fromTag = getStringOrWordOrNumber(); 209 String toTag = getStringOrWordOrNumber(); 210 211 readAttributes(edgesFormat); 212 213 sendEdgeAdded(graphName, tag, fromTag, toTag, false); 214 215 if (attributes != null) { 216 for (String k : attributes.keySet()) { 217 Object value = attributes.get(k); 218 sendEdgeAttributeAdded(graphName, tag, k, value); 219 } 220 } 221 222 if (eatEolOrEof() == StreamTokenizer.TT_EOF) 223 return false; 224 } else if (key.equals("an")) { 225 tag = getStringOrWordOrNumber(); 226 227 readAttributes(nodesFormat); 228 sendNodeAdded(graphName, tag); 229 230 if (attributes != null) { 231 for (String k : attributes.keySet()) { 232 Object value = attributes.get(k); 233 sendNodeAttributeAdded(graphName, tag, k, value); 234 } 235 } 236 237 if (eatEolOrEof() == StreamTokenizer.TT_EOF) 238 return false; 239 } else if (key.equals("de")) { 240 tag = getStringOrWordOrNumber(); 241 242 sendEdgeRemoved(graphName, tag); 243 244 if (eatEolOrEof() == StreamTokenizer.TT_EOF) 245 return false; 246 } else if (key.equals("dn")) { 247 tag = getStringOrWordOrNumber(); 248 249 sendNodeRemoved(graphName, tag); 250 251 if (eatEolOrEof() == StreamTokenizer.TT_EOF) 252 return false; 253 } else if (key.equals("st")) { 254 String w = getWordOrNumber(); 255 256 try { 257 double time = Double.parseDouble(w); 258 259 sendStepBegins(graphName, time); 260 } catch (NumberFormatException e) { 261 parseError("expecting a number after `st', got `" + w + "'"); 262 } 263 264 if (eatEolOrEof() == StreamTokenizer.TT_EOF) 265 return false; 266 } else if (key == "#") { 267 eatAllUntilEol(); 268 } else if (key == "EOL") { 269 return true; 270 } else if (key == "EOF") { 271 return false; 272 } else { 273 parseError("found an unknown key in file '" + key 274 + "' (expecting an,ae,cn,ce,dn,de or st)"); 275 } 276 277 return true; 278 } 279 280 /** 281 * tries to read all the events between 2 steps 282 */ 283 public boolean nextStep() throws IOException { 284 String key = ""; 285 String tag = null; 286 287 while (!key.equals("st") && !key.equals("EOF")) { 288 key = getWordOrSymbolOrStringOrEolOrEof(); 289 290 if (key.equals("ce")) { 291 tag = getStringOrWordOrNumber(); 292 293 readAttributes(edgesFormat); 294 295 for (String k : attributes.keySet()) { 296 Object value = attributes.get(k); 297 sendEdgeAttributeChanged(graphName, tag, k, null, value); 298 } 299 300 if (eatEolOrEof() == StreamTokenizer.TT_EOF) 301 return false; 302 } else if (key.equals("cn")) { 303 tag = getStringOrWordOrNumber(); 304 305 readAttributes(nodesFormat); 306 307 for (String k : attributes.keySet()) { 308 Object value = attributes.get(k); 309 sendNodeAttributeChanged(graphName, tag, k, null, value); 310 } 311 312 if (eatEolOrEof() == StreamTokenizer.TT_EOF) 313 return false; 314 } else if (key.equals("ae")) { 315 tag = getStringOrWordOrNumber(); 316 String fromTag = getStringOrWordOrNumber(); 317 String toTag = getStringOrWordOrNumber(); 318 319 readAttributes(edgesFormat); 320 sendEdgeAdded(graphName, tag, fromTag, toTag, false); 321 322 if (attributes != null) { 323 for (String k : attributes.keySet()) { 324 Object value = attributes.get(k); 325 sendNodeAttributeAdded(graphName, tag, k, value); 326 } 327 } 328 329 if (eatEolOrEof() == StreamTokenizer.TT_EOF) 330 return false; 331 } else if (key.equals("an")) { 332 tag = getStringOrWordOrNumber(); 333 334 readAttributes(nodesFormat); 335 sendNodeAdded(graphName, tag); 336 337 if (attributes != null) { 338 for (String k : attributes.keySet()) { 339 Object value = attributes.get(k); 340 sendNodeAttributeAdded(graphName, tag, k, value); 341 } 342 } 343 344 if (eatEolOrEof() == StreamTokenizer.TT_EOF) 345 return false; 346 } else if (key.equals("de")) { 347 tag = getStringOrWordOrNumber(); 348 349 sendEdgeRemoved(graphName, tag); 350 351 if (eatEolOrEof() == StreamTokenizer.TT_EOF) 352 return false; 353 } else if (key.equals("dn")) { 354 tag = getStringOrWordOrNumber(); 355 356 sendNodeRemoved(graphName, tag); 357 358 if (eatEolOrEof() == StreamTokenizer.TT_EOF) 359 return false; 360 } else if (key.equals("st")) { 361 String w = getWordOrNumber(); 362 363 try { 364 double time = Double.parseDouble(w); 365 sendStepBegins(graphName, time); 366 } catch (NumberFormatException e) { 367 parseError("expecting a number after `st', got `" + w + "'"); 368 } 369 370 if (eatEolOrEof() == StreamTokenizer.TT_EOF) 371 return false; 372 } else if (key == "#") { 373 eatAllUntilEol(); 374 } else if (key == "EOL") { 375 // NOP 376 } else if (key == "EOF") { 377 return false; 378 } else { 379 parseError("found an unknown key in file '" + key 380 + "' (expecting an,ae,cn,ce,dn,de or st)"); 381 } 382 } 383 384 return true; 385 } 386 387 protected void readAttributes(ArrayList<AttributeFormat> formats) 388 throws IOException { 389 attributes.clear(); 390 391 if (formats.size() > 0) { 392 for (AttributeFormat format : formats) { 393 if (format.type == AttributeType.NUMBER) { 394 readNumberAttribute(format.name); 395 } else if (format.type == AttributeType.VECTOR) { 396 readVectorAttribute(format.name); 397 } else if (format.type == AttributeType.STRING) { 398 readStringAttribute(format.name); 399 } 400 } 401 } 402 } 403 404 protected void readNumberAttribute(String name) throws IOException { 405 int tok = st.nextToken(); 406 407 if (isNull(tok)) { 408 attributes.put(name, new Double(0)); 409 } else { 410 st.pushBack(); 411 412 double n = getNumber(); 413 414 attributes.put(name, new Double(n)); 415 } 416 } 417 418 protected void readVectorAttribute(String name) throws IOException { 419 int tok = st.nextToken(); 420 421 if (isNull(tok)) { 422 attributes.put(name, new ArrayList<Double>()); 423 } else { 424 425 boolean loop = true; 426 427 ArrayList<Double> vector = new ArrayList<Double>(); 428 429 while (loop) { 430 if (tok != StreamTokenizer.TT_NUMBER) 431 parseError("expecting a number, " + gotWhat(tok)); 432 433 vector.add(st.nval); 434 435 tok = st.nextToken(); 436 437 if (tok != ',') { 438 loop = false; 439 st.pushBack(); 440 } else { 441 tok = st.nextToken(); 442 } 443 } 444 445 attributes.put(name, vector); 446 } 447 } 448 449 protected void readStringAttribute(String name) throws IOException { 450 String s = getStringOrWordOrNumber(); 451 452 attributes.put(name, s); 453 } 454 455 protected boolean isNull(int tok) { 456 if (tok == StreamTokenizer.TT_WORD) 457 return (st.sval.equals("null")); 458 459 return false; 460 } 461 462 @Override 463 public void begin(String filename) throws IOException { 464 super.begin(filename); 465 init(); 466 } 467 468 @Override 469 public void begin(InputStream stream) throws IOException { 470 super.begin(stream); 471 init(); 472 } 473 474 @Override 475 public void begin(Reader reader) throws IOException { 476 super.begin(reader); 477 init(); 478 } 479 480 @Override 481 public void begin(URL url) throws IOException { 482 super.begin(url); 483 init(); 484 } 485 486 protected void init() throws IOException { 487 st.parseNumbers(); 488 489 String magic = eatOneOfTwoWords("DGS001", "DGS002"); 490 491 if (magic.equals("DGS001")) 492 version = 1; 493 else 494 version = 2; 495 496 eatEol(); 497 graphName = getWord(); 498 stepCountAnnounced = (int) getNumber();// Integer.parseInt( getWord() ); 499 eventCountAnnounced = (int) getNumber();// Integer.parseInt( getWord() 500 // ); 501 eatEol(); 502 503 if (graphName != null) { 504 attributes.clear(); 505 attributes.put("label", graphName); 506 sendGraphAttributeAdded(graphName, "label", graphName); 507 } else { 508 graphName = "DGS_"; 509 } 510 511 graphName = String.format("%s_%d", graphName, 512 System.currentTimeMillis() + ((long) Math.random() * 10)); 513 514 readAttributeFormat(); 515 } 516 517 protected void readAttributeFormat() throws IOException { 518 int tok = st.nextToken(); 519 520 if (tok == StreamTokenizer.TT_WORD && st.sval.equals("nodes")) { 521 parseAttributeFormat(nodesFormat); 522 tok = st.nextToken(); 523 } 524 525 if (tok == StreamTokenizer.TT_WORD && st.sval.equals("edges")) { 526 parseAttributeFormat(edgesFormat); 527 } else { 528 st.pushBack(); 529 } 530 } 531 532 protected void parseAttributeFormat(ArrayList<AttributeFormat> format) 533 throws IOException { 534 int tok = st.nextToken(); 535 536 while (tok != StreamTokenizer.TT_EOL) { 537 if (tok == StreamTokenizer.TT_WORD) { 538 String name = st.sval; 539 540 eatSymbol(':'); 541 542 tok = st.nextToken(); 543 544 if (tok == StreamTokenizer.TT_WORD) { 545 String type = st.sval.toLowerCase(); 546 547 if (type.equals("number") || type.equals("n")) { 548 format.add(new AttributeFormat(name, 549 AttributeType.NUMBER)); 550 } else if (type.equals("string") || type.equals("s")) { 551 format.add(new AttributeFormat(name, 552 AttributeType.STRING)); 553 } else if (type.equals("vector") || type.equals("v")) { 554 format.add(new AttributeFormat(name, 555 AttributeType.VECTOR)); 556 } else { 557 parseError("unknown attribute type `" 558 + type 559 + "' (only `number', `vector' and `string' are accepted)"); 560 } 561 } else { 562 parseError("expecting an attribute type, got `" 563 + gotWhat(tok) + "'"); 564 } 565 } else { 566 parseError("expecting an attribute name, got `" + gotWhat(tok) 567 + "'"); 568 } 569 570 tok = st.nextToken(); 571 } 572 } 573 574 @Override 575 protected void continueParsingInInclude() throws IOException { 576 } 577 578 @Override 579 protected Reader createReaderFrom(String file) throws FileNotFoundException { 580 InputStream is = null; 581 582 try { 583 is = new GZIPInputStream(new FileInputStream(file)); 584 } catch (IOException e) { 585 is = new FileInputStream(file); 586 } 587 588 return new BufferedReader(new InputStreamReader(is)); 589 } 590 591 @Override 592 protected Reader createReaderFrom(InputStream stream) { 593 594 return new BufferedReader(new InputStreamReader(stream)); 595 } 596 597 @Override 598 protected void configureTokenizer(StreamTokenizer tok) throws IOException { 599 if (COMMENT_CHAR > 0) 600 tok.commentChar(COMMENT_CHAR); 601 // tok.quoteChar( QUOTE_CHAR ); 602 tok.eolIsSignificant(eol_is_significant); 603 tok.wordChars('_', '_'); 604 tok.ordinaryChar('1'); 605 tok.ordinaryChar('2'); 606 tok.ordinaryChar('3'); 607 tok.ordinaryChar('4'); 608 tok.ordinaryChar('5'); 609 tok.ordinaryChar('6'); 610 tok.ordinaryChar('7'); 611 tok.ordinaryChar('8'); 612 tok.ordinaryChar('9'); 613 tok.ordinaryChar('0'); 614 tok.ordinaryChar('.'); 615 tok.ordinaryChar('-'); 616 tok.wordChars('1', '1'); 617 tok.wordChars('2', '2'); 618 tok.wordChars('3', '3'); 619 tok.wordChars('4', '4'); 620 tok.wordChars('5', '5'); 621 tok.wordChars('6', '6'); 622 tok.wordChars('7', '7'); 623 tok.wordChars('8', '8'); 624 tok.wordChars('9', '9'); 625 tok.wordChars('0', '0'); 626 tok.wordChars('.', '.'); 627 tok.wordChars('-', '-'); 628 // tok.parseNumbers(); 629 } 630}