/** Propagate the results of adding a link from this node to the target node. */ public void propagateAdd(GraphNode target) { Set<GraphNode> sc = new HashSet<>(target.succClosed); sc.add(target); visitPredecessors( new Visitor<Set<GraphNode>, GraphNode>() { @Override public List<GraphNode> visit( GraphNode node, GraphNode processing, Set<GraphNode> sc, GraphNode target) { // Add closure node.succClosed.addAll(sc); // Scan for redundant links List<GraphNode> kill = null; for (Iterator<GraphNode> i = node.succ.iterator(); i.hasNext(); ) { GraphNode s = i.next(); if (sc.contains(s)) { i.remove(); if (s == processing) { // Can't remove immediately w/o beaking the visitor loop if (kill == null) kill = new ArrayList<>(); kill.add(node); } else { s.pred.remove(node); } } } return kill; } }, sc, target); }
/** Propagate the results of creating a new SCC with this node as lead. */ public void propagateSCC() { Set<GraphNode> visited = new HashSet<>(); visited.add(this); // Scan predecessors not including ourselves doVisitPredecessors( new Visitor<Set<GraphNode>, Object>() { @Override public List<GraphNode> visit( GraphNode node, GraphNode processing, Set<GraphNode> sc, Object ignored) { // Add closure node.succClosed.addAll(sc); // Scan for redundant links List<GraphNode> kill = null; for (Iterator<GraphNode> i = node.succ.iterator(); i.hasNext(); ) { GraphNode s = i.next(); if (sc.contains(s)) { i.remove(); // s.pred.remove(node); if (s == processing) { // Can't remove immediately w/o beaking the visitor loop if (kill == null) kill = new ArrayList<>(); kill.add(node); } else { s.pred.remove(node); } } } return kill; } }, succClosed, null, visited); }
/** Create a list of triples for a given set of successors to this node. */ private List<Triple> triplesForSuccessors(Node base, boolean closed, TransitiveGraphCache tgc) { Set<GraphNode> successors = closed ? succClosed : succ; ArrayList<Triple> result = new ArrayList<>(successors.size() + 10); result.add(new Triple(base, tgc.closedPredicate, base)); // implicit reflexive case for (GraphNode s : successors) { result.add(new Triple(base, tgc.closedPredicate, s.rdfNode)); s.siblings.addSuccessors(base, tgc, result); } siblings.addSuccessors(base, tgc, result); return result; }
/** * Given a set of SCC nodes make this the lead member of the SCC and reroute all incoming and * outgoing links accordingly. This eager rewrite is based on the assumption that there are few * cycles so it is better to rewrite once and keep the graph easy to traverse. */ public void makeLeadNodeFor(Set<GraphNode> members) { // Accumulate all successors Set<GraphNode> newSucc = new HashSet<>(); Set<GraphNode> newSuccClosed = new HashSet<>(); for (GraphNode n : members) { newSucc.addAll(n.succ); newSuccClosed.addAll(n.succClosed); } newSucc.removeAll(members); newSuccClosed.removeAll(members); succ = newSucc; succClosed = newSuccClosed; // Rewrite all direct successors to have us as predecessor for (GraphNode n : succ) { n.pred.removeAll(members); n.pred.add(this); } // Find all predecessor nodes and relink link them to point to us Set<GraphNode> done = new HashSet<>(); Set<GraphNode> newAliases = new HashSet<>(); for (GraphNode member : members) { addSiblings(newAliases, member); } becomeLeaderOf(newAliases); for (GraphNode n : members) { if (n != this) { pred.addAll(n.pred); n.relocateAllRefTo(this, done); n.becomeSubordinateOf(this); } } pred.removeAll(members); }
/** Visit each predecessor of this node applying the given visitor. Breadth first. */ private <Alpha, Beta> void doVisitPredecessors( Visitor<Alpha, Beta> visitor, Alpha arg1, Beta arg2, Set<GraphNode> seen) { if (seen.add(this)) { Collection<GraphNode> allKill = null; for (Iterator<GraphNode> i = pred.iterator(); i.hasNext(); ) { GraphNode pred = i.next(); List<GraphNode> kill = visitor.visit(pred, this, arg1, arg2); if (kill != null) { if (allKill == null) allKill = new ArrayList<GraphNode>(); allKill.addAll(kill); } } if (allKill != null) pred.removeAll(allKill); for (Iterator<GraphNode> i = pred.iterator(); i.hasNext(); ) { GraphNode pred = i.next(); pred.doVisitPredecessors(visitor, arg1, arg2, seen); } } }
@Override void addInto(Set<GraphNode> nodes, GraphNode ignored) { nodes.addAll(components); }
void addInto(Set<GraphNode> nodes, GraphNode m) { nodes.add(m); }
/** Assert an inferred indirect link from this node to the given traget */ public void assertIndirectLinkTo(GraphNode target) { // if (this == target) return; succClosed.add(target); clearTripleCache(); }
/** * Remove a direct link currently from this node to the given target. Does not update the closed * successor cache. */ public void retractLinkTo(GraphNode target) { if (this == target) return; succ.remove(target); target.pred.remove(this); clearTripleCache(); }
/** * Assert a direct link between this node and this given target. Does not update the closed * successor cache */ public void assertLinkTo(GraphNode target) { if (this == target) return; succ.add(target); target.pred.add(this); clearTripleCache(); }
/** * Return an iterator over all the indirect successors of this node. This does NOT include aliases * of successors and is used for graph maintenance. */ public Iterator<GraphNode> iteratorOverSuccessors() { return succClosed.iterator(); }
/** Visit each predecessor of this node applying the given visitor. */ public <Alpha, Beta> void visitPredecessors(Visitor<Alpha, Beta> visitor, Alpha arg1, Beta arg2) { List<GraphNode> kill = visitor.visit(this, null, arg1, arg2); if (kill != null) pred.removeAll(kill); doVisitPredecessors(visitor, arg1, arg2, new HashSet<GraphNode>()); }
/** Return true if there is a direct path from this node to the argument node. */ public boolean directPathTo(GraphNode A) { if (this == A) return true; return succ.contains(A); }
/** Return true if there is a path from this node to the argument node. */ public boolean pathTo(GraphNode A) { if (this == A) return true; return succClosed.contains(A); }
@Override Iterator<GraphNode> siblingIterator() { return components.iterator(); }