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.HashMap;
037import java.util.Iterator;
038
039import org.graphstream.ui.geom.Point3;
040import org.graphstream.ui.graphicGraph.stylesheet.Rule;
041import org.graphstream.ui.graphicGraph.stylesheet.StyleSheet;
042
043/**
044 * Transforms a graph into a SVG description.
045 * 
046 * <p>
047 * Do not confuse this with the SVG export capabilities of the graph viewer. The
048 * SVG export of the viewer provides the most exact copy of what you see on
049 * screen. This class is made to export only nodes and edges without styling to
050 * SVG.
051 * </p>
052 * 
053 * 
054 * <p>
055 * Although there is no styling, each node and edge is put in a SVG group with
056 * the identifier of the corresponding element in the graph. A minimal CSS style
057 * sheet is included in the generated file and it is easy to add another.
058 * </p>
059 */
060public class FileSinkSVG extends FileSinkBase {
061        // Attribute
062
063        /**
064         * The output.
065         */
066        protected PrintWriter out;
067
068        /**
069         * What element ?.
070         */
071        protected enum What {
072                NODE, EDGE, OTHER
073        };
074
075        /**
076         * The positions of each node.
077         */
078        protected HashMap<String, Point3> nodePos = new HashMap<String, Point3>();
079
080        // Construction
081
082        public FileSinkSVG() {
083                // NOP.
084        }
085
086        // Command
087
088        @Override
089        public void end() throws IOException {
090                if (out != null) {
091                        out.flush();
092                        out.close();
093                        out = null;
094                }
095        }
096
097        // Command
098
099        @Override
100        protected void outputHeader() throws IOException {
101                out = (PrintWriter) output;
102
103                out.printf("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>%n");
104                out.printf("<svg" + " xmlns:svg=\"http://www.w3.org/2000/svg\""
105                                + " width=\"100%%\"" + " height=\"100%%\"" + ">%n");
106
107                // TODO
108                // outputStyle( styleSheet );
109        }
110
111        @Override
112        protected void outputEndOfFile() throws IOException {
113                outputNodes();
114                out.printf("</svg>%n");
115        }
116
117        public void edgeAttributeAdded(String graphId, long timeId, String edgeId,
118                        String attribute, Object value) {
119                // NOP
120        }
121
122        public void edgeAttributeChanged(String graphId, long timeId,
123                        String edgeId, String attribute, Object oldValue, Object newValue) {
124                // NOP
125        }
126
127        public void edgeAttributeRemoved(String graphId, long timeId,
128                        String edgeId, String attribute) {
129                // NOP
130        }
131
132        public void graphAttributeAdded(String graphId, long timeId,
133                        String attribute, Object value) {
134                // NOP
135        }
136
137        public void graphAttributeChanged(String graphId, long timeId,
138                        String attribute, Object oldValue, Object newValue) {
139                // NOP
140        }
141
142        public void graphAttributeRemoved(String graphId, long timeId,
143                        String attribute) {
144                // NOP
145        }
146
147        public void nodeAttributeAdded(String graphId, long timeId, String nodeId,
148                        String attribute, Object value) {
149                setNodePos(nodeId, attribute, value);
150        }
151
152        public void nodeAttributeChanged(String graphId, long timeId,
153                        String nodeId, String attribute, Object oldValue, Object newValue) {
154                setNodePos(nodeId, attribute, newValue);
155        }
156
157        public void nodeAttributeRemoved(String graphId, long timeId,
158                        String nodeId, String attribute) {
159                // NOP
160        }
161
162        public void edgeAdded(String graphId, long timeId, String edgeId,
163                        String fromNodeId, String toNodeId, boolean directed) {
164                Point3 p0 = nodePos.get(fromNodeId);
165                Point3 p1 = nodePos.get(toNodeId);
166
167                if (p0 != null && p1 != null) {
168                        out.printf("  <g id=\"%s\">%n", edgeId);
169                        out.printf("    <line x1=\"%f\" y1=\"%f\" x2=\"%f\" y2=\"%f\"/>%n",
170                                        p0.x, p0.y, p1.x, p1.y);
171                        out.printf("  </g>%n");
172                }
173        }
174
175        public void edgeRemoved(String graphId, long timeId, String edgeId) {
176                // NOP
177        }
178
179        public void graphCleared(String graphId, long timeId) {
180                // NOP
181        }
182
183        public void nodeAdded(String graphId, long timeId, String nodeId) {
184                nodePos.put(nodeId, new Point3());
185        }
186
187        public void nodeRemoved(String graphId, long timeId, String nodeId) {
188                nodePos.remove(nodeId);
189        }
190
191        public void stepBegins(String graphId, long timeId, double time) {
192                // NOP
193        }
194
195        // Utility
196
197        protected void setNodePos(String nodeId, String attribute, Object value) {
198                Point3 p = nodePos.get(nodeId);
199
200                if (p == null) {
201                        p = new Point3((float) Math.random(), (float) Math.random(), 0f);
202                        nodePos.put(nodeId, p);
203                }
204
205                if (attribute.equals("x")) {
206                        if (value instanceof Number)
207                                p.x = ((Number) value).floatValue();
208                } else if (attribute.equals("y")) {
209                        if (value instanceof Number)
210                                p.y = ((Number) value).floatValue();
211                } else if (attribute.equals("z")) {
212                        if (value instanceof Number)
213                                p.z = ((Number) value).floatValue();
214                }
215
216                else if (attribute.equals("xy")) {
217                        if (value instanceof Object[]) {
218                                Object xy[] = ((Object[]) value);
219
220                                if (xy.length > 1) {
221                                        p.x = ((Number) xy[0]).floatValue();
222                                        p.y = ((Number) xy[1]).floatValue();
223                                }
224                        }
225                } else if (attribute.equals("xyz")) {
226                        if (value instanceof Object[]) {
227                                Object xyz[] = ((Object[]) value);
228
229                                if (xyz.length > 1) {
230                                        p.x = ((Number) xyz[0]).floatValue();
231                                        p.y = ((Number) xyz[1]).floatValue();
232                                }
233
234                                if (xyz.length > 2) {
235                                        p.z = ((Number) xyz[2]).floatValue();
236                                }
237                        }
238                }
239        }
240
241        protected void outputStyle(String styleSheet) {
242                String style = null;
243
244                if (styleSheet != null) {
245                        StyleSheet ssheet = new StyleSheet();
246
247                        try {
248                                if (styleSheet.startsWith("url(")) {
249                                        styleSheet = styleSheet.substring(5);
250
251                                        int pos = styleSheet.lastIndexOf(')');
252
253                                        styleSheet = styleSheet.substring(0, pos);
254
255                                        ssheet.parseFromFile(styleSheet);
256                                } else {
257                                        ssheet.parseFromString(styleSheet);
258                                }
259
260                                style = styleSheetToSVG(ssheet);
261                        } catch (IOException e) {
262                                e.printStackTrace();
263                                ssheet = null;
264                        }
265                }
266
267                if (style == null)
268                        style = "circle { fill: grey; stroke: none; } line { stroke-width: 1; stroke: black; }";
269
270                out.printf("<defs><style type=\"text/css\"><![CDATA[%n");
271                out.printf("    %s%n", style);
272                out.printf("]]></style></defs>%n");
273        }
274
275        protected void outputNodes() {
276                Iterator<? extends String> keys = nodePos.keySet().iterator();
277
278                while (keys.hasNext()) {
279                        String key = keys.next();
280                        Point3 pos = nodePos.get(key);
281
282                        out.printf("  <g id=\"%s\">%n", key);
283                        out.printf("    <circle cx=\"%f\" cy=\"%f\" r=\"4\"/>%n", pos.x,
284                                        pos.y);
285                        out.printf("  </g>%n");
286                }
287        }
288
289        protected String styleSheetToSVG(StyleSheet sheet) {
290                StringBuilder out = new StringBuilder();
291
292                addRule(out, sheet.getDefaultGraphRule());
293
294                return out.toString();
295        }
296
297        protected void addRule(StringBuilder out, Rule rule) {
298                // Style style = rule.getStyle();
299
300                // TODO
301        }
302}