package i5.las2peer.services.ocd.graphs; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.ElementCollection; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.IdClass; import javax.persistence.JoinColumn; import javax.persistence.MapKeyColumn; import javax.persistence.OneToMany; import javax.persistence.OneToOne; import javax.persistence.PostLoad; import javax.persistence.PrePersist; import javax.persistence.PreUpdate; import javax.persistence.Transient; import org.la4j.matrix.Matrix; import org.la4j.matrix.sparse.CCSMatrix; import i5.las2peer.services.ocd.algorithms.utils.Termmatrix; import y.base.Edge; import y.base.EdgeCursor; import y.base.GraphListener; import y.base.Node; import y.base.NodeCursor; import y.view.Graph2D; /** * Represents a graph (or network), i.e. the node / edge structure and additional meta information. * @author Sebastian * */ @Entity @IdClass(CustomGraphId.class) public class CustomGraph extends Graph2D { /////////////////// DATABASE COLUMN NAMES /* * Database column name definitions. */ protected static final String idColumnName = "ID"; protected static final String userColumnName = "USER_NAME"; private static final String nameColumnName = "NAME"; // private static final String descriptionColumnName = "DESCRIPTION"; // private static final String lastUpdateColumnName = "LAST_UPDATE"; private static final String idEdgeMapKeyColumnName = "RUNTIME_ID"; private static final String idNodeMapKeyColumnName = "RUNTIME_ID"; private static final String creationMethodColumnName = "CREATION_METHOD"; private static final String pathColumnName = "INDEX_PATH"; /* * Field name definitions for JPQL queries. */ public static final String USER_NAME_FIELD_NAME = "userName"; public static final String ID_FIELD_NAME = "id"; public static final String CREATION_METHOD_FIELD_NAME = "creationMethod"; /////////////////////////// ATTRIBUTES /** * System generated persistence id. */ @Id @GeneratedValue(strategy=GenerationType.IDENTITY) @Column(name = idColumnName) private long id; /** * The name of the user owning the graph. */ @Id @Column(name = userColumnName) private String userName = ""; /** * The name of the graph. */ @Column(name = nameColumnName) private String name = ""; /** * The path to the index for the content of each node belonging to the graph. */ @Column(name = pathColumnName) private String path = ""; // /** // * The description of the graph. // */ // @Column(name = descriptionColumnName) // private String description = ""; // /** // * Last time of modification. // */ // @Version // @Column(name = lastUpdateColumnName) // private Timestamp lastUpdate; /** * The graph's types. */ @ElementCollection private Set<Integer> types = new HashSet<Integer>(); /** * The log for the benchmark model the graph was created by. */ @OneToOne(orphanRemoval = true, cascade={CascadeType.ALL}) @JoinColumn(name = creationMethodColumnName) private GraphCreationLog creationMethod = new GraphCreationLog(GraphCreationType.REAL_WORLD, new HashMap<String, String>()); /** * The covers based on this graph. */ @OneToMany(mappedBy = "graph", orphanRemoval = true, cascade={CascadeType.ALL} /*, fetch=FetchType.LAZY */) private List<Cover> covers = new ArrayList<Cover>(); ///////////////////// THE FOLLOWING ATTRIBUTES ARE MAINTAINED AUTOMATICALLY AND ONLY OF INTERNAL / PERSISTENCE USE /* * Mapping from fix node ids to custom nodes for additional node data and persistence. */ @OneToMany(mappedBy = "graph", orphanRemoval = true, cascade={CascadeType.ALL} /*, fetch=FetchType.LAZY */) @MapKeyColumn(name = idNodeMapKeyColumnName) private Map<Integer, CustomNode> customNodes = new HashMap<Integer, CustomNode>(); /* * Mapping from fix edge ids to custom nodes for additional edge data and persistence. */ @OneToMany(mappedBy = "graph", orphanRemoval = true, cascade={CascadeType.ALL} /*, fetch=FetchType.LAZY */) @MapKeyColumn(name = idEdgeMapKeyColumnName) private Map<Integer, CustomEdge> customEdges = new HashMap<Integer, CustomEdge>(); /* * Mapping from edges to fix edge ids. */ @Transient private Map<Edge, Integer> edgeIds = new HashMap<Edge, Integer>(); /* * Mapping from nodes to fix node ids. */ @Transient private Map<Node, Integer> nodeIds = new HashMap<Node, Integer>(); /* * Mapping from custom nodes to nodes. */ @Transient private Map<CustomNode, Node> reverseNodeMap = new HashMap<CustomNode, Node>(); /* * Used for assigning runtime edge indices. */ @Transient private int edgeIndexer = 0; /* * Used for assigning runtime node indices. */ @Transient private int nodeIndexer = 0; @Transient private Termmatrix termMatrix = new Termmatrix(); ///////////////////////////// METHODS AND CONSTRUCTORS /** * Creates a new instance. */ public CustomGraph() { this.addGraphListener(new CustomGraphListener()); } /** * Copy constructor. * @param graph The graph to copy. */ public CustomGraph(Graph2D graph) { super(graph); NodeCursor nodes = this.nodes(); while(nodes.ok()) { Node node = nodes.node(); this.addCustomNode(node); nodes.next(); } EdgeCursor edges = this.edges(); while(edges.ok()) { Edge edge = edges.edge(); this.addCustomEdge(edge); edges.next(); } Iterator<?> listenerIt = this.getGraphListeners(); while(listenerIt.hasNext()) { this.removeGraphListener((GraphListener)listenerIt.next()); listenerIt.remove(); } this.addGraphListener(new CustomGraphListener()); } /** * Copy constructor. * @param graph The graph to copy. */ public CustomGraph(CustomGraph graph) { super(graph); this.creationMethod = new GraphCreationLog(graph.creationMethod.getType(), graph.creationMethod.getParameters()); this.creationMethod.setStatus(graph.creationMethod.getStatus()); this.customNodes = new HashMap<Integer, CustomNode>(); copyMappings(graph.customNodes, graph.customEdges, graph.nodeIds, graph.edgeIds); this.userName = new String(graph.userName); this.name = new String(graph.name); this.id = graph.id; this.path = graph.path; // this.description = new String(graph.description); // if(graph.lastUpdate != null) { // this.lastUpdate = new Timestamp(graph.lastUpdate.getTime()); // } nodeIndexer = graph.nodeIndexer; edgeIndexer = graph.edgeIndexer; this.types = new HashSet<Integer>(graph.types); } /** * Sets all the structural information to that of another graph. * This includes the structure of the nodes and edges, their custom information * and the graph types. * @param graph The graph to obtain data from. */ public void setStructureFrom(CustomGraph graph) { NodeCursor nodes = this.nodes(); Node node; /* * Removes all nodes and edges * including their custom information. */ while(nodes.ok()) { node = nodes.node(); this.removeNode(node); nodes.next(); } /* * Adds new nodes and edges. */ this.nodeIds.clear(); this.customNodes.clear(); this.edgeIds.clear(); this.nodeIds.clear(); this.edgeIndexer = 0; this.nodeIndexer = 0; this.reverseNodeMap.clear(); this.types.clear(); nodes = graph.nodes(); while(nodes.ok()) { node = this.createNode(); this.setNodeName(node, graph.getNodeName(nodes.node())); nodes.next(); } Node[] nodeArr = this.getNodeArray(); EdgeCursor edges = graph.edges(); Edge edge; Edge refEdge; while(edges.ok()) { refEdge = edges.edge(); edge = this.createEdge(nodeArr[refEdge.source().index()], nodeArr[refEdge.target().index()]); this.setEdgeWeight(edge, graph.getEdgeWeight(refEdge)); edges.next(); } /* * Updates graph types. */ for(GraphType type : graph.getTypes()) { this.addType(type); } } /** * Getter for the id. * @return The id. */ public long getId() { return id; } /** * Getter for the user name. * @return The name. */ public String getUserName() { return userName; } /** * Setter for the user name. * @param user The name. */ public void setUserName(String user) { this.userName = user; } /** * Getter for the graph name. * @return The name. */ public String getName() { return name; } /** * Setter for the graph name. * @param name The name. */ public void setName(String name) { this.name = name; } /** * Getter for the graphs path to the index for the node content. * @return The index path. */ public String getPath() { return path; } /** * Setter for the graphs path to the index for the node content. * @param path The index path. */ public void setPath(String path) { this.path = path; } public Termmatrix getTermMatrix(){ return termMatrix; } public void setTermMatrix(Termmatrix t){ this.termMatrix = t; } // public String getDescription() { // return description; // } // // public Timestamp getLastUpdate() { // return lastUpdate; // } // // public void setDescription(String description) { // this.description = description; // } /** * Setter for the creation method. * @param creationMethod The creation method. */ public void setCreationMethod(GraphCreationLog creationMethod) { this.creationMethod = creationMethod; } /** * Getter for the creation method. * @return The creation method. */ public GraphCreationLog getCreationMethod() { return this.creationMethod; } /** * States whether the graph is of a certain type. * @param type The graph type. * @return TRUE if the graph is of the type, otherwise FALSE. */ public boolean isOfType(GraphType type) { return this.types.contains(type.getId()); } /** * Adds a graph type to the graph. * @param type The graph type. */ public void addType(GraphType type) { this.types.add(type.getId()); } /** * Removes a graph type from the graph. * @param type The graph type. */ public void removeType(GraphType type) { this.types.remove(type.getId()); } /** * Removes all graph types from the graph. */ public void clearTypes() { this.types.clear(); } /** * Getter for the graph types. * @return The types. */ public Set<GraphType> getTypes() { Set<GraphType> types = new HashSet<GraphType>(); for(int id : this.types) { types.add(GraphType.lookupType(id)); } return types; } /** * Getter for the edge weight of a certain edge. * @param edge The edge. * @return The edge weight. */ public double getEdgeWeight(Edge edge) { return getCustomEdge(edge).getWeight(); } /** * Setter for the edge weight of a certain edge. * @param edge The edge. * @param weight The edge weight. */ public void setEdgeWeight(Edge edge, double weight) { getCustomEdge(edge).setWeight(weight); } // public long getEdgeId(Edge edge) { // return getCustomEdge(edge).getId(); // } /** * Getter for the node name of a certain node. * @param node The node. * @return The node name. */ public String getNodeName(Node node) { return getCustomNode(node).getName(); } /** * Setter for the node name of a certain node. * @param node The node. * @param name The node name. */ public void setNodeName(Node node, String name) { getCustomNode(node).setName(name); } // public int getNodeId(Node node) { // return getCustomNode(node).getId(); // } /** * Returns weighted in-degree, i.e. the sum of the weights of all incoming edges of a node. * @param node The node. * @return The weighted in-degree. */ public double getWeightedInDegree(Node node) { double inDegree = 0; EdgeCursor inEdges = node.inEdges(); while(inEdges.ok()) { Edge edge = inEdges.edge(); inDegree += getCustomEdge(edge).getWeight(); inEdges.next(); } return inDegree; } /** * Returns positive in-degree, i.e. the weight sum of all positive * incoming edges of a node. * * @param node * The node. * @return The positive in-degree. * * @author YLi */ public double getPositiveInDegree(Node node) { double inDegree = 0; EdgeCursor inEdges = node.inEdges(); while (inEdges.ok()) { Edge edge = inEdges.edge(); if (getCustomEdge(edge).getWeight() > 0) inDegree += getCustomEdge(edge).getWeight(); inEdges.next(); } return inDegree; } /** * Returns positive out-degree, i.e. the weight sum of all positive * outgoing edges of a node. * * @param node * The concerned node. * @return The positive out-degree. * * @author YLi */ public double getPositiveOutDegree(Node node) { double outDegree = 0; EdgeCursor outEdges = node.outEdges(); while (outEdges.ok()) { Edge edge = outEdges.edge(); if (getCustomEdge(edge).getWeight() > 0) outDegree += getCustomEdge(edge).getWeight(); outEdges.next(); } return outDegree; } /** * Returns negative in-degree, i.e. the sum of all negative incoming edges * of a node. * * @param node * The node under observation. * @return The negative in-degree. * * @author YLi */ public double getNegativeInDegree(Node node) { double inDegree = 0; EdgeCursor inEdges = node.inEdges(); while (inEdges.ok()) { Edge edge = inEdges.edge(); if (getCustomEdge(edge).getWeight() < 0) inDegree += getCustomEdge(edge).getWeight(); inEdges.next(); } return inDegree; } /** * Returns negative out-degree, i.e. the sum of all negative outgoing edges * of a node. * * @param node * The node under observation. * @return The negative out-degree. * * @author YLi */ public double getNegativeOutDegree(Node node) { double outDegree = 0; EdgeCursor outEdges = node.outEdges(); while (outEdges.ok()) { Edge edge = outEdges.edge(); if (getCustomEdge(edge).getWeight() < 0) outDegree += getCustomEdge(edge).getWeight(); outEdges.next(); } return outDegree; } /** * Returns the weighted out-degree, i.e. the sum of the weights of all outgoing edges of a node. * @param node The node. * @return The weighted out-degree. */ public double getWeightedOutDegree(Node node) { double outDegree = 0; EdgeCursor outEdges = node.outEdges(); while(outEdges.ok()) { Edge edge = outEdges.edge(); outDegree += getCustomEdge(edge).getWeight(); outEdges.next(); } return outDegree; } /** * Returns the weighted node degree, i.e. the sum of the weights of all incident edges of a node. * @param node The node. * @return The weighted degree. */ public double getWeightedNodeDegree(Node node) { double degree = 0; EdgeCursor edges = node.edges(); while(edges.ok()) { Edge edge = edges.edge(); degree += getCustomEdge(edge).getWeight(); edges.next(); } return degree; } /** * Returns the absolute node degree, i.e. the sum of absolute weights of all * incident edges of a node. * * @param node * The node. * @return The absolute degree. * * @author YLi */ public double getAbsoluteNodeDegree(Node node) { double degree = 0; EdgeCursor edges = node.edges(); Edge edge; while (edges.ok()) { edge = edges.edge(); degree += Math.abs(getCustomEdge(edge).getWeight()); edges.next(); } return degree; } /** * Returns the maximum edge weight of the graph. * @return The maximum edge weight or negative infinity, if there are no edges in the graph. */ public double getMaxEdgeWeight() { double maxWeight = Double.NEGATIVE_INFINITY; double edgeWeight; EdgeCursor edges = this.edges(); while(edges.ok()) { Edge edge = edges.edge(); edgeWeight = getCustomEdge(edge).getWeight(); if(edgeWeight > maxWeight) { maxWeight = edgeWeight; } edges.next(); } return maxWeight; } /** * Returns the minimum edge weight of the graph. * @return The minimum edge weight or positive infinity, if there are no edges in the graph. */ public double getMinEdgeWeight() { double minWeight = Double.POSITIVE_INFINITY; double edgeWeight; EdgeCursor edges = this.edges(); while(edges.ok()) { Edge edge = edges.edge(); edgeWeight = getCustomEdge(edge).getWeight(); if(edgeWeight < minWeight) { minWeight = edgeWeight; } edges.next(); } return minWeight; } /** * Returns the minimum weighted in-degree of the graph. * @return The weighted in-degree or positive infinity if the graph does not contain any nodes. */ public double getMinWeightedInDegree() { double minDegree = Double.POSITIVE_INFINITY; NodeCursor nodes = this.nodes(); double curDegree; while(nodes.ok()) { curDegree = getWeightedInDegree(nodes.node()); if(curDegree < minDegree) { minDegree = curDegree; } nodes.next(); } return minDegree; } /** * Returns the maximum weighted in-degree of the graph. * @return The weighted in-degree or negative infinity if the graph does not contain any nodes. */ public double getMaxWeightedInDegree() { double maxDegree = Double.NEGATIVE_INFINITY; NodeCursor nodes = this.nodes(); double curDegree; while(nodes.ok()) { curDegree = getWeightedInDegree(nodes.node()); if(curDegree > maxDegree) { maxDegree = curDegree; } nodes.next(); } return maxDegree; } ////////////////// THE FOLLOWING METHODS ARE ONLY OF INTERNAL PACKAGE USE AND FOR PERSISTENCE PURPOSES /* * Initializes all node and edge mappings for the copy constructor. * @param customNodes The custom node mapping of the copied custom graph. * @param customEdges The custom edge mapping of the copied custom graph. * @param nodeIds The node id mapping of the copied custom graph. * @param edgeIds The edge id mapping of the copied custom graph. */ protected void copyMappings(Map<Integer, CustomNode> customNodes, Map<Integer, CustomEdge> customEdges, Map<Node, Integer> nodeIds, Map<Edge, Integer> edgeIds) { for(Map.Entry<Integer, CustomNode> entry : customNodes.entrySet()) { this.customNodes.put(entry.getKey(), new CustomNode(entry.getValue())); } for(Map.Entry<Integer, CustomEdge> entry : customEdges.entrySet()) { this.customEdges.put(entry.getKey(), new CustomEdge(entry.getValue())); } Node[] nodeArr = this.getNodeArray(); for(Map.Entry<Node, Integer> entry : nodeIds.entrySet()) { this.nodeIds.put(nodeArr[entry.getKey().index()], entry.getValue()); } NodeCursor nodes = this.nodes(); while(nodes.ok()) { this.reverseNodeMap.put(this.getCustomNode(nodes.node()), nodes.node()); nodes.next(); } Edge [] edgeArr = this.getEdgeArray(); for(Map.Entry<Edge, Integer> entry : edgeIds.entrySet()) { this.edgeIds.put(edgeArr[entry.getKey().index()], entry.getValue()); } } /* * Returns the custom edge object corresponding to an edge. * @param edge An edge which must belong to this graph. * @return The corresponding custom edge object. */ protected CustomEdge getCustomEdge(Edge edge) { int index = edgeIds.get(edge); return customEdges.get(index); } /* * Returns the custom node object corresponding to a node. * @param node A node which must belong to this graph. * @return The corresponding custom node object. */ protected CustomNode getCustomNode(Node node) { int index = nodeIds.get(node); return customNodes.get(index); } /* * Returns the node object corresponding to a custom node. * @param customNode A customNode which must belong to this graph. * @return The corresponding node object. */ protected Node getNode(CustomNode customNode) { return reverseNodeMap.get(customNode); } /* * Creates a new custom node object and maps the node to it. * @param node The node. */ protected void addCustomNode(Node node) { CustomNode customNode = new CustomNode(); this.nodeIds.put(node, this.nodeIndexer); this.customNodes.put(nodeIndexer, customNode); this.reverseNodeMap.put(customNode, node); nodeIndexer++; } /* * Removes the mappings between a node and its custom node object. * @param node */ protected void removeCustomNode(Node node) { CustomNode customNode = this.getCustomNode(node); int id = this.nodeIds.get(node); this.nodeIds.remove(node); this.customNodes.remove(id); this.reverseNodeMap.remove(customNode); } /* * Creates a new custom edge object and maps the edge to it. * @param edge The edge. */ protected void addCustomEdge(Edge edge) { CustomEdge customEdge = new CustomEdge(); this.edgeIds.put(edge, this.edgeIndexer); this.customEdges.put(edgeIndexer, customEdge); edgeIndexer++; } /* * Removes the mapping from an edge to its custom edge. * @param edge */ protected void removeCustomEdge(Edge edge) { int id = this.edgeIds.get(edge); this.edgeIds.remove(edge); this.customEdges.remove(id); } /* * Returns the neighbourhood matrix. * * @return The neighbourhood matrix. * * @author YLi */ public Matrix getNeighbourhoodMatrix() throws InterruptedException { int nodeNumber = this.nodeCount(); Matrix neighbourhoodMatrix = new CCSMatrix(nodeNumber, nodeNumber); EdgeCursor edges = this.edges(); Edge edge; while (edges.ok()) { if (Thread.interrupted()) { throw new InterruptedException(); } edge = edges.edge(); neighbourhoodMatrix.set(edge.source().index(), edge.target().index(), this.getEdgeWeight(edge)); edges.next(); } return neighbourhoodMatrix; } /* * Returns the set of all neighbours of a given node. * * @param node The node under observation. * * @return The neighbour set of the given node. * * @author YLi */ public Set<Node> getNeighbours(Node node) throws InterruptedException { Set<Node> neighbourSet = new HashSet<Node>(); NodeCursor neighbours = node.neighbors(); Node neighbour; while (neighbours.ok()) { if (Thread.interrupted()) { throw new InterruptedException(); } neighbour = neighbours.node(); if (!neighbourSet.contains(neighbour)) { neighbourSet.add(neighbour); } neighbours.next(); } return neighbourSet; } /* * Returns the set of all positive neighbours of a given node. * * @param node The node under observation. * * @return The positive neighbour set of the given node. * * @author YLi */ public Set<Node> getPositiveNeighbours(Node node) throws InterruptedException { Set<Node> positiveNeighbour = new HashSet<Node>(); Set<Node> neighbours = this.getNeighbours(node); for (Node neighbour : neighbours) { /* * if node a->b positive or node b->a positive */ Edge edge = node.getEdgeTo(neighbour); Edge reverseEdge = node.getEdgeFrom(neighbour); if (edge != null) { if (this.getEdgeWeight(edge) > 0) { positiveNeighbour.add(neighbour); continue; } } if (reverseEdge != null) { if (this.getEdgeWeight(reverseEdge) > 0) { positiveNeighbour.add(neighbour); } } } return positiveNeighbour; } /* * Returns the set of all negative neighbours of a given node. * * @param node The node under observation. * * @return The negative neighbour set of the given node. * * @author YLi */ public Set<Node> getNegativeNeighbours(Node node) throws InterruptedException { Set<Node> negativeNeighbour = new HashSet<Node>(); Set<Node> neighbours = this.getNeighbours(node); for (Node neighbour : neighbours) { /* * if node a->b positive or node b->a positive */ Edge edge = node.getEdgeTo(neighbour); Edge reverseEdge = node.getEdgeFrom(neighbour); if (edge != null) { if (this.getEdgeWeight(edge) < 0) { negativeNeighbour.add(neighbour); continue; } } if (reverseEdge != null) { if (this.getEdgeWeight(reverseEdge) < 0) { negativeNeighbour.add(neighbour); } } } return negativeNeighbour; } /* * Returns the set of all positive edges incident to a given node. * * @param node The node under observation. * * @return The positive edge set of the given node. * * @author YLi */ public Set<Edge> getPositiveEdges(Node node) throws InterruptedException { Set<Edge> positiveEdges = new HashSet<Edge>(); EdgeCursor incidentEdges = node.edges(); Edge incidentEdge; while (incidentEdges.ok()) { if (Thread.interrupted()) { throw new InterruptedException(); } incidentEdge = incidentEdges.edge(); if (getCustomEdge(incidentEdge).getWeight() > 0) { positiveEdges.add(incidentEdge); } incidentEdges.next(); } return positiveEdges; } /* * Returns the set of all positive incoming edges for a given node. * * @param node The node under observation. * * @return The positive incoming edge set of the given node. * * @author YLi */ public Set<Edge> getPositiveInEdges(Node node) throws InterruptedException { Set<Edge> positiveInEdges = new HashSet<Edge>(); EdgeCursor incidentInEdges = node.inEdges(); Edge incidentInEdge; while (incidentInEdges.ok()) { if (Thread.interrupted()) { throw new InterruptedException(); } incidentInEdge = incidentInEdges.edge(); if (getCustomEdge(incidentInEdge).getWeight() > 0) { positiveInEdges.add(incidentInEdge); } incidentInEdges.next(); } return positiveInEdges; } /* * Returns the set of all positive outgoing edges for a given node. * * @param node The node under observation. * * @return The positive outgoing edge set of the given node. * * @author YLi */ public Set<Edge> getPositiveOutEdges(Node node) throws InterruptedException { Set<Edge> positiveOutEdges = new HashSet<Edge>(); EdgeCursor incidentOutEdges = node.outEdges(); Edge incidentOutEdge; while (incidentOutEdges.ok()) { if (Thread.interrupted()) { throw new InterruptedException(); } incidentOutEdge = incidentOutEdges.edge(); if (getCustomEdge(incidentOutEdge).getWeight() > 0) { positiveOutEdges.add(incidentOutEdge); } incidentOutEdges.next(); } return positiveOutEdges; } /* * Returns the set of all negative edges incident to a given node. * * @param node The node under observation. * * @return The negative edge set of the given node. * * @author YLi */ public Set<Edge> getNegativeEdges(Node node) throws InterruptedException { Set<Edge> negativeEdges = new HashSet<Edge>(); EdgeCursor incidentEdges = node.edges(); Edge incidentEdge; while (incidentEdges.ok()) { if (Thread.interrupted()) { throw new InterruptedException(); } incidentEdge = incidentEdges.edge(); if (getCustomEdge(incidentEdge).getWeight() < 0) { negativeEdges.add(incidentEdge); } incidentEdges.next(); } return negativeEdges; } /* * Returns the set of all negative incoming edges for a given node. * * @param node The node under observation. * * @return The negative incoming edge set of the given node. * * @author YLi */ public Set<Edge> getNegativeInEdges(Node node) throws InterruptedException { Set<Edge> negativeInEdges = new HashSet<Edge>(); EdgeCursor incidentInEdges = node.inEdges(); Edge incidentInEdge; while (incidentInEdges.ok()) { if (Thread.interrupted()) { throw new InterruptedException(); } incidentInEdge = incidentInEdges.edge(); if (getCustomEdge(incidentInEdge).getWeight() < 0) { negativeInEdges.add(incidentInEdge); } incidentInEdges.next(); } return negativeInEdges; } /* * Returns the set of all negative outgoing edges for a given node. * * @param node The node under observation. * * @return The negative outgoing edge set of the given node. * * @author YLi */ public Set<Edge> getNegativeOutEdges(Node node) throws InterruptedException { Set<Edge> negativeOutEdges = new HashSet<Edge>(); EdgeCursor incidentOutEdges = node.outEdges(); Edge incidentOutEdge; while (incidentOutEdges.ok()) { if (Thread.interrupted()) { throw new InterruptedException(); } incidentOutEdge = incidentOutEdges.edge(); if (getCustomEdge(incidentOutEdge).getWeight() < 0) { negativeOutEdges.add(incidentOutEdge); } incidentOutEdges.next(); } return negativeOutEdges; } /////////////////////////// PERSISTENCE CALLBACK METHODS /* * PostLoad Method. * Creates node and edge objects from the custom nodes and edges and sets the mappings between the two. * The mapping indices are reset to omit rising numbers due to deletions and reinsertions. */ @PostLoad private void postLoad() { List<CustomNode> nodes = new ArrayList<CustomNode>(this.customNodes.values()); this.nodeIds.clear(); this.customNodes.clear(); for(CustomNode customNode : nodes) { Node node = customNode.createNode(this); this.nodeIds.put(node, node.index()); this.customNodes.put(node.index(), customNode); this.reverseNodeMap.put(customNode, node); } List<CustomEdge> edges = new ArrayList<CustomEdge>(this.customEdges.values()); this.edgeIds.clear(); this.customEdges.clear(); for(CustomEdge customEdge : edges) { Edge edge = customEdge.createEdge(this, reverseNodeMap.get(customEdge.getSource()), this.reverseNodeMap.get(customEdge.getTarget())); this.edgeIds.put(edge, edge.index()); this.customEdges.put(edge.index(), customEdge); } nodeIndexer = this.nodeCount(); edgeIndexer = this.edgeCount(); Iterator<?> listenerIt = this.getGraphListeners(); while(listenerIt.hasNext()) { this.removeGraphListener((GraphListener)listenerIt.next()); listenerIt.remove(); } this.addGraphListener(new CustomGraphListener()); } @PrePersist @PreUpdate /* * PrePersist Method. * Writes the attributes of nodes and edges into their corresponding custom nodes and edges. */ protected void prePersist() { NodeCursor nodes = this.nodes(); while(nodes.ok()) { Node node = nodes.node(); this.getCustomNode(node).update(this, node); nodes.next(); } EdgeCursor edges = this.edges(); while(edges.ok()) { Edge edge = edges.edge(); this.getCustomEdge(edge).update(this, edge); edges.next(); } } }