fork(1) download
  1. import java.util.ArrayList;
  2. import java.util.List;
  3. import java.io.BufferedOutputStream;
  4. import java.io.BufferedInputStream;
  5. import java.io.FileInputStream;
  6. import java.io.FileOutputStream;
  7. import java.io.InputStream;
  8. import java.io.IOException;
  9. import java.io.ObjectInput;
  10. import java.io.ObjectInputStream;
  11. import java.io.ObjectOutput;
  12. import java.io.ObjectOutputStream;
  13. import java.io.ObjectStreamField;
  14. import java.io.OutputStream;
  15. import java.io.Serializable;
  16. /**
  17.  * Represents a graph comprised of collection of nodes,
  18.  * a collection of edges, and an indicator as to
  19.  * whether or not the graph is directed.
  20.  */
  21. public class Graph implements Serializable {
  22. /**
  23.   * Serialization version indicator used to determine if
  24.   * a file is compatible with this class.
  25.   */
  26. private static final long serialVersionUID = 1038001L;
  27. /** Default state save/restore file name. */
  28. public static String SERIAL_FILENAME = "graph.ser";
  29. /** Ensure serialization of necessary fields. */
  30. private static final ObjectStreamField[] serialPersistentFields
  31. = { new ObjectStreamField("isDirected", Boolean.class),
  32. new ObjectStreamField("nodes", List.class),
  33. new ObjectStreamField("edges", List.class) };
  34.  
  35. /**
  36.   * The list of nodes in this graph.
  37.   * Each node is represented by a Node object.
  38.   * @serial
  39.   */
  40. private List<Node> nodes = new ArrayList<Node>();
  41.  
  42. /**
  43.   * The list of edges in this graph.
  44.   * Each edge is represented by an Edge object.
  45.   * @serial
  46.   */
  47. private List<Edge> edges = new ArrayList<Edge>();
  48.  
  49. /**
  50.   * Indicator of directedness of this graph;
  51.   * true = directed, false = undirected
  52.   * @serial
  53.   */
  54. private Boolean isDirected = new Boolean(false);
  55.  
  56. /**
  57.   * Construct an undirected graph with no nodes or edges.
  58.   */
  59. public Graph() { }
  60.  
  61. /**
  62.   * Construct an undirected graph with nodes only, no edges.
  63.   * Performs no error checking.
  64.   * @param nodeLabels a list of String objects to be used as node labels
  65.   */
  66. public Graph(List<String> nodeLabels) {
  67. for (String label : nodeLabels) {
  68. this.nodes.add(new Node(label, null));
  69. }
  70. }
  71.  
  72. /**
  73.   * Construct a simple undirected, unweighted graph.
  74.   * Performs no error checking.
  75.   * @param nodeLabels a list of String objects to be used as node labels
  76.   * @param edgeList a list of edges where each edge is specified as a
  77.   * list of two strings representing the start-node-label and
  78.   * end-node-label in that order.
  79.   */
  80. public Graph(List<String> nodeLabels, List<List<String>> edgeList, Boolean simple) {
  81. this(nodeLabels);
  82. for (List<String> edge : edgeList) {
  83. this.edges.add(new Edge(edge.get(0), edge.get(1), false, null, null));
  84. }
  85. }
  86.  
  87. /**
  88.   * Construct a general graph.
  89.   * Performs no error checking.
  90.   * @param nodeLabels a list of String objects to be used as node labels
  91.   * @param nodeCosts a list of Integer objects to be used as cost of each node in nodeLabels
  92.   * @param directed true if this is a directed graph; false otherwise
  93.   * @param edgeList a list of edges where each edge is specified as a
  94.   * list of Strings representing
  95.   * <start-node-label,end-node-label,directedness,edge-label,edge-weight>;
  96.   * edge-label and edge-weight may be null.
  97.   */
  98. public Graph(List<String> nodeLabels,
  99. List<Integer> nodeCosts,
  100. Boolean directed,
  101. List<List<String>> edgeList) {
  102. for (int i = 0; i < nodeLabels.size(); i++) {
  103. this.nodes.add(new Node(nodeLabels.get(i), nodeCosts.get(i)));
  104. }
  105. this.isDirected = directed;
  106. for (List<String> edge : edgeList) {
  107. this.edges.add(new Edge(edge.get(0),
  108. edge.get(1),
  109. Boolean.valueOf(edge.get(2)),
  110. edge.get(3),
  111. Integer.valueOf(edge.get(4))));
  112. }
  113. }
  114.  
  115. /**
  116.   * Retrieve nodes as list of labels.
  117.   * @return list of labels of nodes in this graph
  118.   */
  119. public List<String> getNodeNamesAsStrings() {
  120. List<String> labels = new ArrayList<String>();
  121. for (Node n : this.nodes) {
  122. labels.add(n.getLabel());
  123. }
  124. return labels;
  125. }
  126.  
  127.  
  128. /**
  129.   * Retrieve nodes as list of pairs of Strings.
  130.   * The first element is the node label, the second is the associated cost.
  131.   * @return list of labels of nodes in this graph
  132.   */
  133. public List<List<String>> getNodesAsStrings() {
  134. List<List<String>> nodeStrings = new ArrayList<List<String>>();
  135. for (Node n : this.nodes) {
  136. List<String> nodePair = new ArrayList<String>();
  137. nodePair.add(n.getLabel());
  138. nodePair.add(n.getCost().toString());
  139. nodeStrings.add(nodePair);
  140. }
  141. return nodeStrings;
  142. }
  143.  
  144. /**
  145.   * Retrieve edges as list of lists of five Strings:
  146.   * two node labels, an indicator of directedness,
  147.   * a label, and a weight.
  148.   * If a directed edge, the first of the pair of nodes represents
  149.   * the start node while the second represents the end node.
  150.   * If a directed edge, the indicator is "true";
  151.   * otherwise the indicator is "false".
  152.   * If the edge has no label, it will be shown as "null".
  153.   * If the edge has a weight, it will be given as an integer string;
  154.   * otherwise it will be shown as "null".
  155.   * @return list of edges in this graph
  156.   */
  157. public List<List<String>> getEdgesAsStrings () {
  158. List<List<String>> edgeList = new ArrayList<List<String>>();
  159. for (Edge e : this.edges) {
  160. List<String> oneEdge = new ArrayList<String>();
  161. oneEdge.add(e.getStartNode());
  162. oneEdge.add(e.getEndNode());
  163. oneEdge.add(e.isDirected().toString());
  164. oneEdge.add("" + e.getLabel());
  165. oneEdge.add("" + e.getWeight());
  166. edgeList.add(oneEdge);
  167. }
  168. return edgeList;
  169. }
  170.  
  171. /**
  172.   * Disclose if graph is directed or undirected.
  173.   * @return true if graph is directed; false otherwise
  174.   */
  175. public Boolean isDirected() { return this.isDirected; }
  176.  
  177. /**
  178.   * Simple string representation of graph.
  179.   * @return a string that shows the nodes and edges of this graph
  180.   */
  181. @Override
  182. public String toString() {
  183. return "GRAPH: " +
  184. (isDirected ? "Directed" : "Undirected") +
  185. "\n " +
  186. "Nodes(" + nodes + ")\n " +
  187. "Edges(" + edges + ")";
  188. }
  189.  
  190. @Override public boolean equals(Object o) {
  191. if ( this == o ) return true;
  192. if ( !(o instanceof Graph) ) return false;
  193. Graph that = (Graph)o;
  194. return
  195. (this.nodes.equals(that.nodes)) &&
  196. (this.edges.equals(that.edges)) &&
  197. (this.isDirected.equals(that.isDirected));
  198. }
  199.  
  200. @Override public int hashCode() {
  201. return
  202. 17 +
  203. ((this.nodes == null) ? 0 : this.nodes.hashCode()) +
  204. ((this.edges == null) ? 0 : this.edges.hashCode()) +
  205. ((this.isDirected == null) ? 0 : this.isDirected.hashCode());
  206. }
  207.  
  208. /**
  209.   * Save the state of this graph.
  210.   * @param graphFileName the name of the file into which to save,
  211.   * or null to use default file name.
  212.   */
  213. public void saveGraph(String graphFileName) {
  214. if (graphFileName == null) graphFileName = Graph.SERIAL_FILENAME;
  215. // Serialize the graph.
  216. try {
  217. OutputStream file = new FileOutputStream(graphFileName);
  218. OutputStream buffer = new BufferedOutputStream(file);
  219. ObjectOutput output = new ObjectOutputStream(buffer);
  220. try { output.writeObject(this); }
  221. finally { output.close(); }
  222. }
  223. catch (IOException ex) {
  224. System.err.println("Unsuccessful save. " + ex);
  225. }
  226.  
  227. // Attempt to deserialize the graph as verification.
  228. try {
  229. InputStream file = new FileInputStream(graphFileName);
  230. InputStream buffer = new BufferedInputStream(file);
  231. ObjectInput input = new ObjectInputStream (buffer);
  232. try {
  233.  
  234. @SuppressWarnings("unchecked") // Accommodate type erasure.
  235. Graph restored = null;
  236. restored = (Graph)input.readObject();
  237. // Simple check that deserialized data matches original.
  238. if (!this.toString().equals(restored.toString()))
  239. System.err.println("[1] State restore did not match save!");
  240. if (!this.equals(restored))
  241. System.err.println("[2] State restore did not match save!");
  242. }
  243. finally { input.close(); }
  244. }
  245. System.err.println(
  246. "Unsuccessful deserialization: Class not found. " + ex);
  247. }
  248. catch (IOException ex) {
  249. System.err.println("Unsuccessful deserialization: " + ex);
  250. }
  251. }
  252.  
  253. /**
  254.   * Reload this graph.
  255.   * @param graphFileName the name of the file with serialized graph,
  256.   * or null to use default file name.
  257.   */
  258. public void restoreGraph(String graphFileName) {
  259. if (graphFileName == null) graphFileName = Graph.SERIAL_FILENAME;
  260. Graph restored = null;
  261. try {
  262. InputStream file = new FileInputStream(graphFileName);
  263. InputStream buffer = new BufferedInputStream(file);
  264. ObjectInput input = new ObjectInputStream (buffer);
  265. try {
  266. @SuppressWarnings("unchecked") // Accommodate type erasure.
  267. Graph retrieved = (Graph)input.readObject();
  268. restored = retrieved;
  269. }
  270. finally {
  271. input.close();
  272. }
  273. }
  274. System.err.println(
  275. "Unsuccessful deserialization: Class not found. " + ex);
  276. }
  277. catch (IOException ex) {
  278. System.err.println("Unsuccessful deserialization: " + ex);
  279. }
  280. if (restored == null) {
  281. System.err.println("Unsuccessful deserialization: restored == null");
  282. } else {
  283. this.nodes = restored.nodes;
  284. this.edges = restored.edges;
  285. this.isDirected = restored.isDirected;
  286. }
  287. }
  288.  
  289. /**
  290.   * Node representation is comprised of a
  291.   * String label and an Integer cost.
  292.   * If cost is null, this node is not part of
  293.   * a graph whose nodes have a cost attribute.
  294.   */
  295. private class Node implements Serializable {
  296. private String label;
  297. private Integer cost;
  298. /**
  299.   * Construct a node with attributes as
  300.   * specified by the parameters.
  301.   * @param nodeLabel the label associated with this node
  302.   * @param nodeCost the cost or weight associated with this node
  303.   */
  304. Node(String nodeLabel, Integer nodeCost) {
  305. this.label = nodeLabel;
  306. this.cost = nodeCost;
  307. }
  308.  
  309. String getLabel() { return this.label; }
  310.  
  311. Integer getCost() { return this.cost; }
  312.  
  313. @Override public String toString() { return this.label; }
  314.  
  315. /**
  316.   * Equals predicate helper method.
  317.   * @param x first object for comparison
  318.   * @param y second object for comparison
  319.   * @return true if both objects are null or
  320.   * objects' native equals method returns true;
  321.   * false otherwise
  322.   */
  323. private boolean closeEnough(Object x, Object y) {
  324. if (x == y) return true;
  325. if (x == null || y == null) return false;
  326. return x.equals(y);
  327. }
  328.  
  329. @Override public boolean equals(Object o) {
  330. if ( this == o ) return true;
  331. if ( !(o instanceof Node) ) return false;
  332. Node that = (Node)o;
  333. return
  334. closeEnough(this.label, that.label) &&
  335. closeEnough(this.cost, that.cost);
  336. }
  337.  
  338. @Override public int hashCode() {
  339. return
  340. 29 +
  341. ((this.label == null) ? 0 : this.label.hashCode()) +
  342. ((this.cost == null) ? 0 : this.cost.hashCode());
  343. }
  344. }
  345.  
  346. /**
  347.   * Edge representation is comprised of two String
  348.   * node labels, a Boolean indicator of directedness,
  349.   * an optional Integer weight, and an optional
  350.   * String edge label.
  351.   * Nodes are referred to as start and end even
  352.   * if that distinction is not meaningful (as for
  353.   * undirected edges).
  354.   * If weight is null, this edge is not part of
  355.   * a graph whose edges have a weight attribute.
  356.   */
  357. private class Edge implements Serializable {
  358. private String start;
  359. private String end;
  360. private Boolean directed;
  361. private String label;
  362. private Integer weight;
  363.  
  364. /**
  365.   * Construct an edge with attributes as
  366.   * specified by the parameters.
  367.   * @param startNode the label of the start node
  368.   * @param endNode the label of the end node
  369.   * @param isDirected true if a directed edge; false otherwise
  370.   * @param edgeLabel the label of this edge (null if none)
  371.   * @param edgeWeight the integer weight of this edge (null if none)
  372.   */
  373. Edge(String startNode, String endNode, Boolean isDirected,
  374. String edgeLabel, Integer edgeWeight) {
  375. this.start = startNode;
  376. this.end = endNode;
  377. this.directed = isDirected;
  378. this.label = edgeLabel;
  379. this.weight = edgeWeight;
  380. }
  381.  
  382. String getStartNode() { return this.start; }
  383.  
  384. String getEndNode() { return this.end; }
  385.  
  386. Boolean isDirected() { return this.directed; }
  387.  
  388. String getLabel() { return this.label; }
  389.  
  390. Integer getWeight() { return this.weight; }
  391.  
  392. @Override public String toString() {
  393. return "<" + this.start + "," + this.end + "," +
  394. this.directed + "," + this.label + "," + this.weight + ">";
  395. }
  396.  
  397. /**
  398.   * Equals predicate helper method.
  399.   * @param x first object for comparison
  400.   * @param y second object for comparison
  401.   * @return true if both objects are null or
  402.   * objects' native equals method returns true;
  403.   * false otherwise
  404.   */
  405. boolean closeEnough(Object x, Object y) {
  406. if (x == y) return true;
  407. if (x == null || y == null) return false;
  408. return x.equals(y);
  409. }
  410.  
  411. @Override public boolean equals(Object o) {
  412. if ( this == o ) return true;
  413. if ( !(o instanceof Edge) ) return false;
  414. Edge that = (Edge)o;
  415. return
  416. closeEnough(this.start, that.start) &&
  417. closeEnough(this.end, that.end) &&
  418. closeEnough(this.directed, that.directed) &&
  419. closeEnough(this.label, that.label) &&
  420. closeEnough(this.weight, that.weight);
  421. }
  422.  
  423. @Override public int hashCode() {
  424. return
  425. 37 +
  426. ((this.start == null) ? 0 : this.start.hashCode()) +
  427. ((this.end == null) ? 0 : this.end.hashCode()) +
  428. ((this.directed == null) ? 0 : this.directed.hashCode()) +
  429. ((this.label == null) ? 0 : this.label.hashCode()) +
  430. ((this.weight == null) ? 0 : this.weight.hashCode());
  431. }
  432. }
  433. }
  434.  
Not running #stdin #stdout 0s 0KB
stdin
Standard input is empty
stdout
Standard output is empty