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}