Java tutorial
/* * Created on June 16, 2008 * * Copyright (c) 2008, the JUNG Project and the Regents of the University * of California * All rights reserved. * * This software is open-source under the BSD license; see either * "license.txt" or * http://jung.sourceforge.net/license.txt for a description. */ package edu.uci.ics.jung.io; import java.io.BufferedWriter; import java.io.IOException; import java.io.Writer; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Map; import org.apache.commons.collections15.Transformer; import org.apache.commons.collections15.TransformerUtils; import edu.uci.ics.jung.graph.Graph; import edu.uci.ics.jung.graph.Hypergraph; import edu.uci.ics.jung.graph.UndirectedGraph; import edu.uci.ics.jung.graph.util.EdgeType; import edu.uci.ics.jung.graph.util.Pair; /** * Writes graphs out in GraphML format. * * Current known issues: * <ul> * <li/>Only supports one graph per output file. * <li/>Does not indent lines for text-format readability. * </ul> * */ public class GraphMLWriter<V, E> { protected Transformer<V, String> vertex_ids; protected Transformer<E, String> edge_ids; protected Map<String, GraphMLMetadata<Hypergraph<V, E>>> graph_data; protected Map<String, GraphMLMetadata<V>> vertex_data; protected Map<String, GraphMLMetadata<E>> edge_data; protected Transformer<V, String> vertex_desc; protected Transformer<E, String> edge_desc; protected Transformer<Hypergraph<V, E>, String> graph_desc; protected boolean directed; protected int nest_level; /** * */ @SuppressWarnings("unchecked") public GraphMLWriter() { vertex_ids = new Transformer<V, String>() { public String transform(V v) { return v.toString(); } }; edge_ids = TransformerUtils.nullTransformer(); graph_data = Collections.emptyMap(); vertex_data = Collections.emptyMap(); edge_data = Collections.emptyMap(); vertex_desc = TransformerUtils.nullTransformer(); edge_desc = TransformerUtils.nullTransformer(); graph_desc = TransformerUtils.nullTransformer(); nest_level = 0; } /** * * @param graph * @param w * @throws IOException */ public void save(Hypergraph<V, E> graph, Writer w) throws IOException { BufferedWriter bw = new BufferedWriter(w); // write out boilerplate header bw.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"); bw.write("<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns/graphml\"\n" + "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" \n"); bw.write("xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns/graphml\">\n"); // write out data specifiers, including defaults for (String key : graph_data.keySet()) writeKeySpecification(key, "graph", graph_data.get(key), bw); for (String key : vertex_data.keySet()) writeKeySpecification(key, "node", vertex_data.get(key), bw); for (String key : edge_data.keySet()) writeKeySpecification(key, "edge", edge_data.get(key), bw); // write out graph-level information // set edge default direction bw.write("<graph edgedefault=\""); directed = !(graph instanceof UndirectedGraph); if (directed) bw.write("directed\">\n"); else bw.write("undirected\">\n"); // write graph description, if any String desc = graph_desc.transform(graph); if (desc != null) bw.write("<desc>" + desc + "</desc>\n"); // write graph data out if any for (String key : graph_data.keySet()) { Transformer<Hypergraph<V, E>, ?> t = graph_data.get(key).transformer; Object value = t.transform(graph); if (value != null) bw.write(format("data", "key", key, value.toString()) + "\n"); } // write vertex information writeVertexData(graph, bw); // write edge information writeEdgeData(graph, bw); // close graph bw.write("</graph>\n"); bw.write("</graphml>\n"); bw.flush(); bw.close(); } // public boolean save(Collection<Hypergraph<V,E>> graphs, Writer w) // { // return true; // } protected void writeIndentedText(BufferedWriter w, String to_write) throws IOException { for (int i = 0; i < nest_level; i++) w.write(" "); w.write(to_write); } protected void writeVertexData(Hypergraph<V, E> graph, BufferedWriter w) throws IOException { for (V v : graph.getVertices()) { String v_string = String.format("<node id=\"%s\"", vertex_ids.transform(v)); boolean closed = false; // write description out if any String desc = vertex_desc.transform(v); if (desc != null) { w.write(v_string + ">\n"); closed = true; w.write("<desc>" + desc + "</desc>\n"); } // write data out if any for (String key : vertex_data.keySet()) { Transformer<V, ?> t = vertex_data.get(key).transformer; if (t != null) { Object value = t.transform(v); if (value != null) { if (!closed) { w.write(v_string + ">\n"); closed = true; } w.write(format("data", "key", key, value.toString()) + "\n"); } } } if (!closed) w.write(v_string + "/>\n"); // no contents; close the node with "/>" else w.write("</node>\n"); } } protected void writeEdgeData(Hypergraph<V, E> g, Writer w) throws IOException { for (E e : g.getEdges()) { Collection<V> vertices = g.getIncidentVertices(e); String id = edge_ids.transform(e); String e_string; boolean is_hyperedge = !(g instanceof Graph); if (is_hyperedge) { e_string = "<hyperedge "; // add ID if present if (id != null) e_string += "id=\"" + id + "\" "; } else { Pair<V> endpoints = new Pair<V>(vertices); V v1 = endpoints.getFirst(); V v2 = endpoints.getSecond(); e_string = "<edge "; // add ID if present if (id != null) e_string += "id=\"" + id + "\" "; // add edge type if doesn't match default EdgeType edge_type = g.getEdgeType(e); if (directed && edge_type == EdgeType.UNDIRECTED) e_string += "directed=\"false\" "; if (!directed && edge_type == EdgeType.DIRECTED) e_string += "directed=\"true\" "; e_string += "source=\"" + vertex_ids.transform(v1) + "\" target=\"" + vertex_ids.transform(v2) + "\""; } boolean closed = false; // write description out if any String desc = edge_desc.transform(e); if (desc != null) { w.write(e_string + ">\n"); closed = true; w.write("<desc>" + desc + "</desc>\n"); } // write data out if any for (String key : edge_data.keySet()) { Transformer<E, ?> t = edge_data.get(key).transformer; Object value = t.transform(e); if (value != null) { if (!closed) { w.write(e_string + ">\n"); closed = true; } w.write(format("data", "key", key, value.toString()) + "\n"); } } // if this is a hyperedge, write endpoints out if any if (is_hyperedge) { for (V v : vertices) { if (!closed) { w.write(e_string + ">\n"); closed = true; } w.write("<endpoint node=\"" + vertex_ids.transform(v) + "\"/>\n"); } } if (!closed) w.write(e_string + "/>\n"); // no contents; close the edge with "/>" else if (is_hyperedge) w.write("</hyperedge>\n"); else w.write("</edge>\n"); } } protected void writeKeySpecification(String key, String type, GraphMLMetadata<?> ds, BufferedWriter bw) throws IOException { bw.write("<key id=\"" + key + "\" for=\"" + type + "\""); boolean closed = false; // write out description if any String desc = ds.description; if (desc != null) { if (!closed) { bw.write(">\n"); closed = true; } bw.write("<desc>" + desc + "</desc>\n"); } // write out default if any Object def = ds.default_value; if (def != null) { if (!closed) { bw.write(">\n"); closed = true; } bw.write("<default>" + def.toString() + "</default>\n"); } if (!closed) bw.write("/>\n"); else bw.write("</key>\n"); } protected String format(String type, String attr, String value, String contents) { return String.format("<%s %s=\"%s\">%s</%s>", type, attr, value, contents, type); } /** * Provides an ID that will be used to identify a vertex in the output file. * If the vertex IDs are not set, the ID for each vertex will default to * the output of <code>toString</code> * (and thus not guaranteed to be unique). * * @param vertex_ids */ public void setVertexIDs(Transformer<V, String> vertex_ids) { this.vertex_ids = vertex_ids; } /** * Provides an ID that will be used to identify an edge in the output file. * If any edge ID is missing, no ID will be written out for the * corresponding edge. * * @param edge_ids */ public void setEdgeIDs(Transformer<E, String> edge_ids) { this.edge_ids = edge_ids; } /** * Provides a map from data type name to graph data. */ public void setGraphData(Map<String, GraphMLMetadata<Hypergraph<V, E>>> graph_map) { graph_data = graph_map; } /** * Provides a map from data type name to vertex data. */ public void setVertexData(Map<String, GraphMLMetadata<V>> vertex_map) { vertex_data = vertex_map; } /** * Provides a map from data type name to edge data. */ public void setEdgeData(Map<String, GraphMLMetadata<E>> edge_map) { edge_data = edge_map; } /** * Adds a new graph data specification. */ public void addGraphData(String id, String description, String default_value, Transformer<Hypergraph<V, E>, String> graph_transformer) { if (graph_data.equals(Collections.EMPTY_MAP)) graph_data = new HashMap<String, GraphMLMetadata<Hypergraph<V, E>>>(); graph_data.put(id, new GraphMLMetadata<Hypergraph<V, E>>(description, default_value, graph_transformer)); } /** * Adds a new vertex data specification. */ public void addVertexData(String id, String description, String default_value, Transformer<V, String> vertex_transformer) { if (vertex_data.equals(Collections.EMPTY_MAP)) vertex_data = new HashMap<String, GraphMLMetadata<V>>(); vertex_data.put(id, new GraphMLMetadata<V>(description, default_value, vertex_transformer)); } /** * Adds a new edge data specification. */ public void addEdgeData(String id, String description, String default_value, Transformer<E, String> edge_transformer) { if (edge_data.equals(Collections.EMPTY_MAP)) edge_data = new HashMap<String, GraphMLMetadata<E>>(); edge_data.put(id, new GraphMLMetadata<E>(description, default_value, edge_transformer)); } /** * Provides vertex descriptions. */ public void setVertexDescriptions(Transformer<V, String> vertex_desc) { this.vertex_desc = vertex_desc; } /** * Provides edge descriptions. */ public void setEdgeDescriptions(Transformer<E, String> edge_desc) { this.edge_desc = edge_desc; } /** * Provides graph descriptions. */ public void setGraphDescriptions(Transformer<Hypergraph<V, E>, String> graph_desc) { this.graph_desc = graph_desc; } }