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}