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.dgs; 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 047import org.graphstream.stream.file.FileSource; 048import org.graphstream.stream.file.FileSourceBase; 049 050/** 051 * Class responsible for parsing files in the DGS format. 052 * 053 * <p> 054 * The DGS file format is especially designed for storing dynamic graph 055 * definitions into a file. More information about the DGS file format will be 056 * found on the GraphStream web site: <a 057 * href="http://graphstream-project.org/">http://graphstream-project.org/</a> 058 * </p> 059 * 060 * The usual file name extension used for this format is ".dgs". 061 * 062 * @see FileSource 063 */ 064public class OldFileSourceDGS extends FileSourceBase { 065 // Attribute 066 067 /** 068 * Format version. 069 */ 070 protected int version; 071 072 /** 073 * Name of the graph. 074 */ 075 protected String graphName; 076 077 /** 078 * Number of step given in the header. 079 */ 080 protected int stepCountAnnounced; 081 082 /** 083 * Number of events given in the header. 084 */ 085 protected int eventCountAnnounced; 086 087 /** 088 * Real number of step at current time. 089 */ 090 protected int stepCount; 091 092 /** 093 * Real number of events at current time. 094 */ 095 protected int eventCount; 096 097 /** 098 * An attribute set used everywhere. 099 */ 100 protected HashMap<String, Object> attributes = new HashMap<String, Object>(); 101 102 /** 103 * True as soon as the end of file is reached. 104 */ 105 protected boolean finished; 106 107 // Construction 108 109 /** 110 * New reader for the DGS graph file format version 3. 111 */ 112 public OldFileSourceDGS() { 113 super(true /* EOL is significant */); 114 } 115 116 // Command -- Parsing 117 118 @Override 119 public boolean nextEvents() throws IOException { 120 if (finished) 121 return false; 122 123 return next(false, false); 124 } 125 126 public boolean nextStep() throws IOException { 127 if (finished) 128 return false; 129 130 return next(true, false); 131 } 132 133 /** 134 * Read either one event or several. 135 * 136 * @param readSteps 137 * If true, read several events (usually starting with a step 138 * event, but it may be preceded by other events), until another 139 * step is encountered. 140 * @param stop 141 * If true stop at the next step encountered (and push it back so 142 * that is is readable at the next call to this method). 143 * @return True if it remains things to read. 144 */ 145 protected boolean next(boolean readSteps, boolean stop) throws IOException { 146 String key = null; 147 boolean loop = readSteps; 148 149 // Sorted in probability of appearance ... 150 151 do { 152 key = getWordOrSymbolOrStringOrEolOrEof(); 153 154 if (key.equals("ce")) { 155 readCE(); 156 } else if (key.equals("cn")) { 157 readCN(); 158 } else if (key.equals("ae")) { 159 readAE(); 160 } else if (key.equals("an")) { 161 readAN(); 162 } else if (key.equals("de")) { 163 readDE(); 164 } else if (key.equals("dn")) { 165 readDN(); 166 } else if (key.equals("cg")) { 167 readCG(); 168 } else if (key.equals("st")) { 169 if (readSteps) { 170 if (stop) { 171 loop = false; 172 pushBack(); 173 } else { 174 stop = true; 175 readST(); 176 } 177 } else { 178 readST(); 179 } 180 } else if (key.equals("#")) { 181 eatAllUntilEol(); 182 return next(readSteps, stop); 183 } else if (key.equals("EOL")) { 184 // Probably an empty line. 185 // NOP 186 return next(readSteps, stop); 187 } else if (key.equals("EOF")) { 188 finished = true; 189 return false; 190 } else { 191 parseError("unknown token '" + key + "'"); 192 } 193 } while (loop); 194 195 return true; 196 } 197 198 protected void readCE() throws IOException { 199 String tag = getStringOrWordOrNumber(); 200 201 readAttributes(attributes); 202 203 for (String key : attributes.keySet()) { 204 Object value = attributes.get(key); 205 206 if (value == null) 207 sendEdgeAttributeRemoved(graphName, tag, key); 208 else 209 sendEdgeAttributeChanged(graphName, tag, key, null, value); 210 } 211 212 if (eatEolOrEof() == StreamTokenizer.TT_EOF) 213 pushBack(); 214 } 215 216 protected void readCN() throws IOException { 217 String tag = getStringOrWordOrNumber(); 218 219 readAttributes(attributes); 220 221 for (String key : attributes.keySet()) { 222 Object value = attributes.get(key); 223 224 if (value == null) 225 sendNodeAttributeRemoved(graphName, tag, key); 226 else 227 sendNodeAttributeChanged(graphName, tag, key, null, value); 228 } 229 230 if (eatEolOrEof() == StreamTokenizer.TT_EOF) 231 pushBack(); 232 } 233 234 protected void readCG() throws IOException { 235 readAttributes(attributes); 236 237 for (String key : attributes.keySet()) { 238 Object value = attributes.get(key); 239 240 if (value == null) 241 sendGraphAttributeRemoved(graphName, key); 242 else 243 sendGraphAttributeChanged(graphName, key, null, value); 244 } 245 246 if (eatEolOrEof() == StreamTokenizer.TT_EOF) 247 pushBack(); 248 } 249 250 protected void readAE() throws IOException { 251 int dir = 0; 252 boolean directed = false; 253 String dirc = null; 254 String tag = null; 255 String fromTag = null; 256 String toTag = null; 257 258 tag = getStringOrWordOrNumber(); 259 fromTag = getStringOrWordOrNumber(); 260 dirc = getWordOrSymbolOrNumberOrStringOrEolOrEof(); 261 262 if (dirc.equals(">")) { 263 directed = true; 264 dir = 1; 265 } else if (dirc.equals("<")) { 266 directed = true; 267 dir = 2; 268 } else { 269 pushBack(); 270 } 271 272 toTag = getStringOrWordOrNumber(); 273 274 if (dir == 2) { 275 String tmp = toTag; 276 toTag = fromTag; 277 fromTag = tmp; 278 } 279 280 readAttributes(attributes); 281 sendEdgeAdded(graphName, tag, fromTag, toTag, directed); 282 283 for (String key : attributes.keySet()) { 284 Object value = attributes.get(key); 285 sendEdgeAttributeAdded(graphName, tag, key, value); 286 } 287 288 if (eatEolOrEof() == StreamTokenizer.TT_EOF) 289 pushBack(); 290 } 291 292 protected void readAN() throws IOException { 293 String tag = getStringOrWordOrNumber(); 294 295 readAttributes(attributes); 296 297 sendNodeAdded(graphName, tag); 298 299 for (String key : attributes.keySet()) { 300 Object value = attributes.get(key); 301 sendNodeAttributeAdded(graphName, tag, key, value); 302 } 303 304 if (eatEolOrEof() == StreamTokenizer.TT_EOF) 305 pushBack(); 306 } 307 308 protected void readDE() throws IOException { 309 String tag = getStringOrWordOrNumber(); 310 311 sendEdgeRemoved(graphName, tag); 312 313 if (eatEolOrEof() == StreamTokenizer.TT_EOF) 314 pushBack(); 315 } 316 317 protected void readDN() throws IOException { 318 String tag = getStringOrWordOrNumber(); 319 320 sendNodeRemoved(graphName, tag); 321 322 if (eatEolOrEof() == StreamTokenizer.TT_EOF) 323 pushBack(); 324 } 325 326 protected void readST() throws IOException { 327 String w = getWordOrNumber(); 328 329 try { 330 double time = Double.parseDouble(w); 331 332 sendStepBegins(graphName, time); 333 } catch (NumberFormatException e) { 334 parseError("expecting a number after `st', got `" + w + "'"); 335 } 336 337 if (eatEolOrEof() == StreamTokenizer.TT_EOF) 338 pushBack(); 339 } 340 341 protected void readAttributes(HashMap<String, Object> attributes) 342 throws IOException { 343 boolean del = false; 344 String key = getWordOrSymbolOrStringOrEolOrEof(); 345 346 attributes.clear(); 347 348 if (key.equals("-")) { 349 key = getWordOrSymbolOrStringOrEolOrEof(); 350 del = true; 351 } 352 353 if (key.equals("+")) 354 key = getWordOrSymbolOrStringOrEolOrEof(); 355 356 while (!key.equals("EOF") && !key.equals("EOL") && !key.equals("]")) { 357 if (del) 358 attributes.put(key, null); 359 else 360 attributes.put(key, readAttributeValue(key)); 361 362 key = getWordOrSymbolOrStringOrEolOrEof(); 363 364 if (key.equals("-")) { 365 key = getWordOrStringOrEolOrEof(); 366 del = true; 367 } 368 369 if (key.equals("+")) { 370 key = getWordOrStringOrEolOrEof(); 371 del = false; 372 } 373 } 374 375 pushBack(); 376 } 377 378 /** 379 * Read an attribute. The "key" (attribute name) is already read. 380 * 381 * @param key 382 * The attribute name, already read. 383 */ 384 protected Object readAttributeValue(String key) throws IOException { 385 ArrayList<Object> vector = null; 386 Object value = null; 387 Object value2 = null; 388 String next = null; 389 390 if (key != null) 391 eatSymbols(":="); 392 393 value = getStringOrWordOrSymbolOrNumberO(); 394 395 if (value.equals("[")) { 396 HashMap<String, Object> map = new HashMap<String, Object>(); 397 398 readAttributes(map); 399 ; 400 eatSymbol(']'); 401 402 value = map; 403 } else if (value.equals("{")) { 404 vector = readAttributeArray(key); 405 eatSymbol('}'); 406 } else { 407 pushBack(); 408 409 value = getStringOrWordOrNumberO(); 410 411 if (key != null) { 412 next = getWordOrSymbolOrNumberOrStringOrEolOrEof(); 413 414 while (next.equals(",")) { 415 if (vector == null) { 416 vector = new ArrayList<Object>(); 417 vector.add(value); 418 } 419 420 value2 = getStringOrWordOrNumberO(); 421 next = getWordOrSymbolOrNumberOrStringOrEolOrEof(); 422 423 vector.add(value2); 424 } 425 426 pushBack(); 427 } 428 } 429 430 if (vector != null) 431 return vector.toArray(); 432 else 433 return value; 434 } 435 436 /** 437 * Read a list of values. 438 * 439 * @param key 440 * attribute key 441 * @return a vector 442 * @throws IOException 443 */ 444 protected ArrayList<Object> readAttributeArray(String key) 445 throws IOException { 446 ArrayList<Object> list = new ArrayList<Object>(); 447 448 Object value; 449 String next; 450 451 do { 452 value = readAttributeValue(null); 453 next = getWordOrSymbolOrNumberOrStringOrEolOrEof(); 454 455 list.add(value); 456 } while (next.equals(",")); 457 458 pushBack(); 459 460 return list; 461 } 462 463 // Command -- Basic parsing 464 465 @Override 466 public void begin(String filename) throws IOException { 467 super.begin(filename); 468 begin(); 469 } 470 471 @Override 472 public void begin(URL url) throws IOException { 473 super.begin(url); 474 begin(); 475 } 476 477 @Override 478 public void begin(InputStream stream) throws IOException { 479 super.begin(stream); 480 begin(); 481 } 482 483 @Override 484 public void begin(Reader reader) throws IOException { 485 super.begin(reader); 486 begin(); 487 } 488 489 protected void begin() throws IOException { 490 st.parseNumbers(); 491 eatWords("DGS003", "DGS004"); 492 493 version = 3; 494 495 eatEol(); 496 graphName = getWordOrString(); 497 stepCountAnnounced = (int) getNumber();// Integer.parseInt( getWord() ); 498 eventCountAnnounced = (int) getNumber();// Integer.parseInt( getWord() 499 // ); 500 eatEol(); 501 502 if (graphName != null) 503 sendGraphAttributeAdded(graphName, "label", graphName); 504 else 505 graphName = "DGS_"; 506 507 graphName = String.format("%s_%d", graphName, 508 System.currentTimeMillis() + ((long) Math.random() * 10)); 509 } 510 511 @Override 512 protected void continueParsingInInclude() throws IOException { 513 } 514 515 @Override 516 protected Reader createReaderFrom(String file) throws FileNotFoundException { 517 InputStream is = null; 518 519 is = new FileInputStream(file); 520 521 if (is.markSupported()) 522 is.mark(128); 523 524 try { 525 is = new GZIPInputStream(is); 526 } catch (IOException e1) { 527 // 528 // This is not a gzip input. 529 // But gzip has eat some bytes so we reset the stream 530 // or close and open it again. 531 // 532 if (is.markSupported()) { 533 try { 534 is.reset(); 535 } catch (IOException e2) { 536 // 537 // Dirty but we hope do not get there 538 // 539 e2.printStackTrace(); 540 } 541 } else { 542 try { 543 is.close(); 544 } catch (IOException e2) { 545 // 546 // Dirty but we hope do not get there 547 // 548 e2.printStackTrace(); 549 } 550 551 is = new FileInputStream(file); 552 } 553 } 554 555 return new BufferedReader(new InputStreamReader(is)); 556 } 557 558 @Override 559 protected Reader createReaderFrom(InputStream stream) { 560 return new BufferedReader(new InputStreamReader(stream)); 561 } 562 563 @Override 564 protected void configureTokenizer(StreamTokenizer tok) throws IOException { 565 if (COMMENT_CHAR > 0) 566 tok.commentChar(COMMENT_CHAR); 567 // tok.quoteChar( QUOTE_CHAR ); 568 tok.eolIsSignificant(eol_is_significant); 569 tok.parseNumbers(); 570 tok.wordChars('_', '_'); 571 } 572}