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.IOException;
035import java.io.InputStream;
036import java.io.Reader;
037import java.net.URL;
038import java.util.HashSet;
039
040/**
041 * Reader for the "edge" graph format.
042 * 
043 * <p>
044 * The edge graph format is a very simple and lightweight format where each line
045 * describes an edge by giving two node names. The nodes are created implicitly.
046 * </p>
047 * 
048 * <p>
049 * This reader also understands the derivative format where a line contains a
050 * first node name, followed by several node names separated by spaces. In this
051 * case it links the first node with all other node name following on the line.
052 * </p>
053 * 
054 * <p>
055 * Also, the format does not specify any direction for edges. By default all
056 * edges are undirected. You can choose to make all edges directed by passing
057 * "true" as the first arguments to constructors
058 * {@link #FileSourceEdge(boolean)} or {@link #FileSourceEdge(boolean, boolean)}
059 * . The direction of edges goes from the first node name on each line toward
060 * the second (or more) node names on each line.
061 * </p>
062 * 
063 * <p>
064 * This format only contains edges. To ensure the "add node" events are sent
065 * before an edge referencing two nodes is created via an "add edge" event, this
066 * reader has a hash set of already encountered nodes. The hash set allows to
067 * issue "add node" events only when a node is encountered for the first time.
068 * </p>
069 * 
070 * </p> This hash set consumes memory, but is the only way to ensure "add node"
071 * events are correctly issued. If this input is directly connected to a graph,
072 * as graphs can create non-existing nodes automatically, you can disable the
073 * hash set of nodes using the constructor
074 * {@link #FileSourceEdge(boolean, boolean)}, and giving "false" for the second
075 * argument. </p>
076 * 
077 * The usual file name extension for this format is ".edge".
078 */
079public class FileSourceEdge extends FileSourceBase {
080        // Attribute
081
082        /**
083         * Allocator for edge identifiers.
084         */
085        protected int edgeid = 0;
086
087        /**
088         * By default, consider edges as undirected.
089         */
090        protected boolean directed = false;
091
092        /**
093         * Set of existing nodes (if nodes are declared).
094         */
095        protected HashSet<String> nodes;
096
097        protected String graphName = "EDGE_";
098
099        // Construction
100
101        /**
102         * New reader for the "edge" format.
103         */
104        public FileSourceEdge() {
105                this(false);
106        }
107
108        /**
109         * New reader for the "edge" format.
110         * 
111         * @param edgesAreDirected
112         *            If true (default=false) edges are considered directed.
113         */
114        public FileSourceEdge(boolean edgesAreDirected) {
115                this(edgesAreDirected, true);
116        }
117
118        /**
119         * New reader for the "edge" format.
120         * 
121         * @param edgesAreDirected
122         *            If true (default=false) edges are considered directed.
123         * @param declareNodes
124         *            If true (default=true) this reader outputs nodeAdded events.
125         */
126        public FileSourceEdge(boolean edgesAreDirected, boolean declareNodes) {
127                directed = edgesAreDirected;
128                nodes = declareNodes ? new HashSet<String>() : null;
129        }
130
131        // Commands
132
133        @Override
134        protected void continueParsingInInclude() throws IOException {
135                // Should not happen, EDGE files cannot be nested.
136        }
137
138        @Override
139        public boolean nextEvents() throws IOException {
140                String id1 = getWordOrNumberOrStringOrEolOrEof();
141
142                if (id1.equals("EOL")) {
143                        // Empty line.
144                } else if (id1.equals("EOF")) {
145                        return false;
146                } else {
147                        declareNode(id1);
148
149                        String id2 = getWordOrNumberOrStringOrEolOrEof();
150
151                        while (!id2.equals("EOL")) {
152                                if (!id1.equals(id2)) {
153                                        String edgeId = Integer.toString(edgeid++);
154
155                                        declareNode(id2);
156                                        sendEdgeAdded(graphName, edgeId, id1, id2, directed);
157                                }
158
159                                id2 = getWordOrNumberOrStringOrEolOrEof();
160                        }
161                }
162
163                return true;
164        }
165
166        protected void declareNode(String id) {
167                if (nodes != null) {
168                        if (!nodes.contains(id)) {
169                                sendNodeAdded(graphName, id);
170                                nodes.add(id);
171                        }
172                }
173        }
174
175        @Override
176        public void begin(String filename) throws IOException {
177                super.begin(filename);
178                init();
179        }
180
181        @Override
182        public void begin(URL url) throws IOException {
183                super.begin(url);
184                init();
185        }
186
187        @Override
188        public void begin(InputStream stream) throws IOException {
189                super.begin(stream);
190                init();
191        }
192
193        @Override
194        public void begin(Reader reader) throws IOException {
195                super.begin(reader);
196                init();
197        }
198
199        protected void init() throws IOException {
200                st.eolIsSignificant(true);
201                st.commentChar('#');
202
203                graphName = String.format("%s_%d", graphName,
204                                System.currentTimeMillis() + ((long) Math.random() * 10));
205        }
206
207        public boolean nextStep() throws IOException {
208                return nextEvents();
209        }
210
211        @Override
212        public void end() throws IOException {
213                super.end();
214        }
215}