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.PrintWriter; 036import java.util.Locale; 037import java.util.regex.Pattern; 038 039/** 040 * Transform the input events into a GML graph. 041 * 042 * <p> 043 * THIS CLASS IS REALLY NOT APPROPRIATE FOR GENERAL USE. Indeed the GML format 044 * is not dynamic and it is very difficult to export the correct attributes of 045 * nodes if the declaration of the attribute is far from the declaration of the 046 * node. The only way would be to store the graph in a buffer and output it at 047 * once when the file is closed. 048 * </p> 049 * 050 * <p> 051 * Therefore this class outputs attributes of nodes and edges only if their 052 * addition directly follows the corresponding node or edge. 053 * </p> 054 */ 055public class FileSinkGML extends FileSinkBase { 056 // Attributes 057 058 /** Alias on the output OutputStream. */ 059 protected PrintWriter out; 060 061 protected String nodeToFinish = null; 062 063 protected String edgeToFinish = null; 064 065 // Construction 066 067 public FileSinkGML() { 068 // NOP 069 } 070 071 // File format events 072 073 @Override 074 protected void outputHeader() throws IOException { 075 out = (PrintWriter) output; 076 077 out.printf("graph [%n"); 078 } 079 080 @Override 081 protected void outputEndOfFile() throws IOException { 082 ensureToFinish(); 083 out.printf("]%n"); 084 } 085 086 // Attribute events 087 088 public void graphAttributeAdded(String sourceId, long timeId, 089 String attribute, Object value) { 090 ensureToFinish(); 091 092 String val = valueToString(value); 093 attribute = keyToString(attribute); 094 095 if (val != null) { 096 out.printf("\t%s %s%n", attribute, val); 097 } 098 } 099 100 public void graphAttributeChanged(String sourceId, long timeId, 101 String attribute, Object oldValue, Object newValue) { 102 ensureToFinish(); 103 // GML is not a dynamic file format ? 104 } 105 106 public void graphAttributeRemoved(String sourceId, long timeId, 107 String attribute) { 108 ensureToFinish(); 109 // GML is not a dynamic file format ? 110 } 111 112 public void nodeAttributeAdded(String sourceId, long timeId, String nodeId, 113 String attribute, Object value) { 114 if (nodeToFinish != null && nodeToFinish.equals(nodeId)) { 115 String val = valueToString(value); 116 attribute = keyToString(attribute); 117 118 if (val != null) { 119 out.printf("\t\t%s %s%n", attribute, val); 120 } 121 } else { 122 ensureToFinish(); 123 } 124 } 125 126 public void nodeAttributeChanged(String sourceId, long timeId, 127 String nodeId, String attribute, Object oldValue, Object newValue) { 128 if (edgeToFinish != null) 129 ensureToFinish(); 130 // GML is not a dynamic file format ? 131 } 132 133 public void nodeAttributeRemoved(String sourceId, long timeId, 134 String nodeId, String attribute) { 135 if (edgeToFinish != null) 136 ensureToFinish(); 137 // GML is not a dynamic file format ? 138 } 139 140 public void edgeAttributeAdded(String sourceId, long timeId, String edgeId, 141 String attribute, Object value) { 142 if (edgeToFinish != null && edgeToFinish.equals(edgeId)) { 143 String val = valueToString(value); 144 attribute = keyToString(attribute); 145 146 if (val != null) { 147 out.printf("\t\t%s %s%n", attribute, val); 148 } 149 } else { 150 ensureToFinish(); 151 } 152 } 153 154 public void edgeAttributeChanged(String sourceId, long timeId, 155 String edgeId, String attribute, Object oldValue, Object newValue) { 156 if (nodeToFinish != null) 157 ensureToFinish(); 158 // GML is not a dynamic file format ? 159 } 160 161 public void edgeAttributeRemoved(String sourceId, long timeId, 162 String edgeId, String attribute) { 163 if (nodeToFinish != null) 164 ensureToFinish(); 165 // GML is not a dynamic file format ? 166 } 167 168 // Element events 169 170 public void nodeAdded(String sourceId, long timeId, String nodeId) { 171 ensureToFinish(); 172 out.printf("\tnode [%n"); 173 out.printf("\t\tid \"%s\"%n", nodeId); 174 nodeToFinish = nodeId; 175 } 176 177 public void nodeRemoved(String sourceId, long timeId, String nodeId) { 178 ensureToFinish(); 179 } 180 181 public void edgeAdded(String sourceId, long timeId, String edgeId, 182 String fromNodeId, String toNodeId, boolean directed) { 183 ensureToFinish(); 184 out.printf("\tedge [%n"); 185 out.printf("\t\tid \"%s\"%n", edgeId); 186 out.printf("\t\tsource \"%s\"%n", fromNodeId); 187 out.printf("\t\ttarget \"%s\"%n", toNodeId); 188 edgeToFinish = edgeId; 189 } 190 191 public void edgeRemoved(String sourceId, long timeId, String edgeId) { 192 ensureToFinish(); 193 } 194 195 public void graphCleared(String sourceId, long timeId) { 196 // Ah ah ah !! 197 } 198 199 public void stepBegins(String sourceId, long timeId, double step) { 200 // NOP 201 } 202 203 // Commands 204 205 Pattern forbiddenKeyChars = Pattern.compile(".*[^a-zA-Z0-9-_.].*"); 206 207 protected String keyToString(String key) { 208 if (forbiddenKeyChars.matcher(key).matches()) 209 return "\"" + key.replace("\"", "\\\"") + "\""; 210 211 return key; 212 } 213 214 protected String valueToString(Object value) { 215 if (value == null) 216 return null; 217 218 if (value instanceof Number) { 219 double val = ((Number) value).doubleValue(); 220 if ((val - ((int) val)) == 0) 221 return String.format(Locale.US, "%d", (int) val); 222 else 223 return String.format(Locale.US, "%f", val); 224 } 225 226 return String.format("\"%s\"", value.toString().replaceAll("\n|\r|\"", " ")); 227 } 228 229 protected void ensureToFinish() { 230 assert ((nodeToFinish != null && edgeToFinish == null) 231 || (nodeToFinish == null && edgeToFinish != null) || (nodeToFinish == null && edgeToFinish == null)); 232 233 if (nodeToFinish != null || edgeToFinish != null) { 234 out.printf("\t]%n"); 235 nodeToFinish = null; 236 edgeToFinish = null; 237 } 238 } 239}