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}