Java tutorial
/* * MyGraphMLWriter.java * * This work is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published * by the Free Software Foundation; either version 2 of the License, * or (at your option) any later version. * * This work is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA * * Copyright (c) 2010-2016 iTransformers Labs. All rights reserved. */ package net.itransformers.utils; 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; import edu.uci.ics.jung.io.GraphMLMetadata; import org.apache.commons.collections15.Transformer; import org.apache.commons.collections15.TransformerUtils; import org.apache.commons.lang.StringEscapeUtils; 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; /** * 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 MyGraphMLWriter<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 MyGraphMLWriter() { 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:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"> \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, StringEscapeUtils.escapeXml(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, StringEscapeUtils.escapeXml(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; } }