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 "ncol" graph format. 042 * 043 * <p> 044 * The ncol graph format is a simple format where each line 045 * describes an edge by giving two node names and an optional third 046 * parameters giving the edge weight. The nodes are created implicitly. 047 * </p> 048 * 049 * <p> 050 * Also, the format does not specify any direction for edges. By default all 051 * edges are undirected. It is specified in the format that you will never 052 * have directed edges and that the lines: 053 * <pre> 054 * node1Name node2Name 055 * </pre> 056 * and 057 * <pre> 058 * node2Name node1Name 059 * </pre> 060 * Cannot both appear at the same time in a file. 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 #FileSourceNCol(boolean)}, and giving "false" for the first 075 * argument. </p> 076 * 077 * The usual file name extension for this format is ".ncol". 078 */ 079public class FileSourceNCol extends FileSourceBase { 080 // Attribute 081 082 /** 083 * Allocator for edge identifiers. 084 */ 085 protected int edgeid = 0; 086 087 /** 088 * Set of existing nodes (if nodes are declared). 089 */ 090 protected HashSet<String> nodes; 091 092 protected String graphName = "NCOL_"; 093 094 // Construction 095 096 /** 097 * New reader for the "ncol" format. 098 */ 099 public FileSourceNCol() { 100 this(false); 101 } 102 103 /** 104 * New reader for the "ncol" format. 105 * 106 * @param declareNodes 107 * If true (default=true) this reader outputs nodeAdded events. 108 */ 109 public FileSourceNCol(boolean declareNodes) { 110 nodes = declareNodes ? new HashSet<String>() : null; 111 } 112 113 // Commands 114 115 @Override 116 protected void continueParsingInInclude() throws IOException { 117 // Should not happen, NCol files cannot be nested. 118 } 119 120 @Override 121 public boolean nextEvents() throws IOException { 122 String id1 = getWordOrNumberOrStringOrEolOrEof(); 123 124 if (id1.equals("EOL")) { 125 // Empty line. Skip it. 126 } else if (id1.equals("EOF")) { 127 return false; 128 } else { 129 declareNode(id1); 130 131 String id2 = getWordOrNumberOrStringOrEolOrEof(); 132 133 if(!id2.equals("EOL") && !id2.equals("EOF")) { 134 // Loops are not accepted by the format. 135 if (!id1.equals(id2)) { 136 // There may be a weight. 137 String weight = getWordOrNumberOrStringOrEolOrEof(); 138 double w = 0.0; 139 140 if(weight.equals("EOL") || weight.equals("EOF")) { 141 weight = null; 142 pushBack(); 143 } else { 144 try { 145 w = Double.parseDouble(weight); 146 } catch(Exception e) { 147 throw new IOException(String.format("cannot transform weight %s into a number", weight)); 148 } 149 } 150 151 String edgeId = Integer.toString(edgeid++); 152 153 declareNode(id2); 154 sendEdgeAdded(graphName, edgeId, id1, id2, false); 155 156 if(weight != null) 157 sendEdgeAttributeAdded(graphName, edgeId, "weight", (Double)w); 158 } 159 } else { 160 throw new IOException("unexpected EOL or EOF"); 161 } 162 } 163 164 return true; 165 } 166 167 protected void declareNode(String id) { 168 if (nodes != null) { 169 if (!nodes.contains(id)) { 170 sendNodeAdded(graphName, id); 171 nodes.add(id); 172 } 173 } 174 } 175 176 @Override 177 public void begin(String filename) throws IOException { 178 super.begin(filename); 179 init(); 180 } 181 182 @Override 183 public void begin(URL url) throws IOException { 184 super.begin(url); 185 init(); 186 } 187 188 @Override 189 public void begin(InputStream stream) throws IOException { 190 super.begin(stream); 191 init(); 192 } 193 194 @Override 195 public void begin(Reader reader) throws IOException { 196 super.begin(reader); 197 init(); 198 } 199 200 protected void init() throws IOException { 201 st.eolIsSignificant(true); 202 st.commentChar('#'); 203 204 graphName = String.format("%s_%d", graphName, 205 System.currentTimeMillis() + ((long) Math.random() * 10)); 206 } 207 208 public boolean nextStep() throws IOException { 209 return nextEvents(); 210 } 211 212 @Override 213 public void end() throws IOException { 214 super.end(); 215 } 216}