KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > hp > hpl > jena > reasoner > transitiveReasoner > TransitiveGraphCache


1 /******************************************************************
2  * File: TransitiveGraphCacheNew.java
3  * Created by: Dave Reynolds
4  * Created on: 16-Nov-2004
5  *
6  * (c) Copyright 2004, 2005 Hewlett-Packard Development Company, LP
7  * [See end of file]
8  * $Id: TransitiveGraphCache.java,v 1.18 2005/02/21 12:18:36 andy_seaborne Exp $
9  *****************************************************************/

10
11 package com.hp.hpl.jena.reasoner.transitiveReasoner;
12
13 import com.hp.hpl.jena.graph.*;
14 import com.hp.hpl.jena.util.iterator.*;
15 import com.hp.hpl.jena.reasoner.*;
16
17 import java.util.*;
18
19 /**
20  * Datastructure used to represent a closed transitive reflexive relation.
21  * It (mostly) incrementally maintains a transitive reduction and transitive
22  * closure of the relationship and so queries should be faster than dynamically
23  * computing the closed or reduced relations.
24  * <p>
25  * The implementation stores the reduced and closed relations as real graph
26  * (objects linked together by pointers). For each graph node we store its direct
27  * predecessors and successors and its closed successors. A cost penalty
28  * is the storage turnover involved in turning the graph representation back into
29  * triples to answer queries. We could avoid this by optionally also storing the
30  * manifested triples for the links.
31  * </p><p>
32  * Cycles are currently handled by collapsing strongly connected components.
33  * Incremental deletes would be possible but at the price of substanially
34  * more storage and code complexity. We compromise by doing the easy cases
35  * incrementally but some deletes (those that break strongly connected components)
36  * will trigger a fresh rebuild.
37  * </p><p>
38  * TODO Combine this with interval indexes (Agrawal, Borigda and Jagadish 1989)
39  * for storing the closure of the predecessor relationship. Typical graphs
40  * will be nearly tree shaped so the successor closure is modest (L^2 where
41  * L is the depth of the tree branch) but the predecessor closure would be
42  * expensive. The interval index would handle predecessor closure nicely.
43  * </p>
44  * @author <a HREF="mailto:der@hplb.hpl.hp.com">Dave Reynolds</a>
45  * @version $Revision: 1.18 $
46  */

47
48 // Note to maintainers. The GraphNode object is treated as a record structure
49
// rather than an abstract datatype by the rest of the GraphCache code - which
50
// directly access its structure. I justify this on the flimsy grounds that it is a
51
// private inner class.
52

53 public class TransitiveGraphCache implements Finder {
54
55     /** Flag controlling the whether the triples
56      * representing the closed relation should also be cached. */

57     protected boolean cacheTriples = false;
58     
59     /** Map from RDF Node to the corresponding Graph node. */
60     protected HashMap nodeMap = new HashMap();
61     
62     /** The RDF predicate representing the direct relation */
63     protected Node directPredicate;
64     
65     /** The RDF predicate representing the closed relation */
66     protected Node closedPredicate;
67     
68     /** A list of pending deletes which break the cycle-free normal form */
69     protected Set deletesPending;
70     
71     /** The original triples, needed for processing delete operations
72      * because some information is lost in the SCC process */

73     protected Set originalTriples = new HashSet();
74     
75     /**
76      * Inner class used to represent vistors than can be applied to each
77      * node in a graph walk.
78      */

79     static interface Visitor {
80         void visit(GraphNode node, Object JavaDoc arg1, Object JavaDoc arg2);
81     }
82     
83     /**
84      * Inner class used to represent the graph node structure.
85      * Rather fat nodes (four sets)
86      */

87     private static class GraphNode {
88         /** The RDF Graph Node this corresponds to */
89         protected Node rdfNode;
90         
91         /** The list of direct successor nodes to this node */
92         protected Set succ = new HashSet();
93         
94         /** The list of direct predecessors nodes */
95         protected Set pred = new HashSet();
96         
97         /** The set of all transitive successor nodes to this node */
98         protected Set succClosed = new HashSet();
99         
100         /** An optional cache of the triples that represent succClosed */
101         protected List succClosedTriples;
102         
103         /** Null for simple nodes. For the lead node in a SCC will be a list
104          * of all the nodes in the SCC. For non-lead nodes it will be a ref to the lead node. */

105         protected Object JavaDoc aliases;
106
107         /**
108          * Constructor.
109          */

110         public GraphNode(Node node) {
111             rdfNode = node;
112         }
113         
114         /**
115          * Return true if there is a path from this node to the argument node.
116          */

117         public boolean pathTo(GraphNode A) {
118             if (this == A) return true;
119             return succClosed.contains(A);
120         }
121
122         /**
123          * Return true if there is a direct path from this node to the argument node.
124          */

125         public boolean directPathTo(GraphNode A) {
126             if (this == A) return true;
127             return succ.contains(A);
128         }
129         
130         /**
131          * Return the lead node in the strongly connected component containing this node.
132          * It will be the node itself if it is a singleton or the lead node.
133          */

134         public GraphNode leadNode() {
135             if (aliases != null && aliases instanceof GraphNode) {
136                 return ((GraphNode)aliases).leadNode();
137             } else {
138                 return this;
139             }
140         }
141         
142         /**
143          * Visit each predecessor of this node applying the given visitor.
144          */

145         public void visitPredecessors(Visitor visitor, Object JavaDoc arg1, Object JavaDoc arg2) {
146             visitor.visit(this, arg1, arg2);
147             doVisitPredecessors(visitor, arg1, arg2, new HashSet());
148         }
149         
150         /**
151          * Visit each predecessor of this node applying the given visitor.
152          * Breadth first.
153          */

154         private void doVisitPredecessors(Visitor visitor, Object JavaDoc arg1, Object JavaDoc arg2, Set seen) {
155             if (seen.add(this)) {
156                 for (Iterator i = pred.iterator(); i.hasNext(); ) {
157                     GraphNode pred = (GraphNode)i.next();
158                     visitor.visit(pred, arg1, arg2);
159                 }
160                 for (Iterator i = pred.iterator(); i.hasNext(); ) {
161                     GraphNode pred = (GraphNode)i.next();
162                     pred.doVisitPredecessors(visitor, arg1, arg2, seen);
163                 }
164             }
165         }
166         
167         /**
168          * Return an iterator over all the indirect successors of this node.
169          * This does NOT include aliases of successors and is used for graph maintenance.
170          */

171         public Iterator iteratorOverSuccessors() {
172             return succClosed.iterator();
173         }
174         
175         /**
176          * Assert a direct link between this node and this given target.
177          * Does not update the closed successor cache
178          */

179         public void assertLinkTo(GraphNode target) {
180             if (this == target) return;
181             succ.add(target);
182             target.pred.add(this);
183             clearTripleCache();
184         }
185         
186         /**
187          * Remove a direct link currently from this node to the given target.
188          * Does not update the closed successor cache.
189          */

190         public void retractLinkTo(GraphNode target) {
191             if (this == target) return;
192             succ.remove(target);
193             target.pred.remove(this);
194             clearTripleCache();
195         }
196         
197         /**
198          * Assert an inferred indirect link from this node to the given traget
199          */

200         public void assertIndirectLinkTo(GraphNode target) {
201 // if (this == target) return;
202
succClosed.add(target);
203             clearTripleCache();
204         }
205         
206         /**
207          * Clear the option cache of the closure triples.
208          */

209         public void clearTripleCache() {
210             succClosedTriples = null;
211         }
212         
213         /**
214          * Propagate the results of adding a link from this
215          * node to the target node.
216          */

217         public void propagateAdd(GraphNode target) {
218             Set sc = target.succClosed;
219             visitPredecessors(new Visitor() {
220                 public void visit(GraphNode node, Object JavaDoc arg1, Object JavaDoc target) {
221                     Set sc = (Set)arg1;
222                     // Add closure
223
node.succClosed.addAll(sc);
224                     node.succClosed.add(target);
225                     // Scan for redundant links
226
for (Iterator i = node.succ.iterator(); i.hasNext();) {
227                         GraphNode s = (GraphNode)i.next();
228                         if (sc.contains(s)) {
229                             i.remove();
230                             s.pred.remove(node);
231                         }
232                     }
233                 }
234             }, sc, target);
235         }
236         
237         /**
238          * Propagate the results of creating a new SCC with this
239          * node as lead.
240          */

241         public void propagateSCC() {
242             Set visited = new HashSet();
243             visited.add(this);
244             // Scan predecessors not including ourselves
245
doVisitPredecessors(new Visitor() {
246                 public void visit(GraphNode node, Object JavaDoc arg1, Object JavaDoc arg2) {
247                     Set sc = (Set)arg1;
248                     // Add closure
249
node.succClosed.addAll(sc);
250                     // Scan for redundant links
251
for (Iterator i = node.succ.iterator(); i.hasNext();) {
252                         GraphNode s = (GraphNode)i.next();
253                         if (sc.contains(s)) {
254                             i.remove();
255                             s.pred.remove(node);
256                         }
257                     }
258                 }
259             }, succClosed, null, visited);
260         }
261         
262         /**
263          * Given a set of SCC nodes make this the lead member of the SCC and
264          * reroute all incoming and outgoing links accordingly.
265          * This eager rewrite is based on the assumption that there are few cycles
266          * so it is better to rewrite once and keep the graph easy to traverse.
267          */

268         public void makeLeadNodeFor(Set members) {
269             // Accumulate all successors
270
Set newSucc = new HashSet();
271             Set newSuccClosed = new HashSet();
272             for (Iterator i = members.iterator(); i.hasNext(); ) {
273                 GraphNode n = (GraphNode)i.next();
274                 newSucc.addAll(n.succ);
275                 newSuccClosed.addAll(n.succClosed);
276             }
277             newSucc.removeAll(members);
278             newSuccClosed.removeAll(members);
279             succ = newSucc;
280             succClosed = newSuccClosed;
281             
282             // Rewrite all direct successors to have us as predecessor
283
for (Iterator i = succ.iterator(); i.hasNext();) {
284                 GraphNode n = (GraphNode)i.next();
285                 n.pred.removeAll(members);
286                 n.pred.add(this);
287             }
288             
289             // Find all predecessor nodes and relink link them to point to us
290
Set done = new HashSet();
291             Set newAliases = new HashSet();
292             for (Iterator i = members.iterator(); i.hasNext(); ) {
293                 GraphNode m = (GraphNode)i.next();
294                 if (m.aliases instanceof Set) {
295                     newAliases.addAll((Set)m.aliases);
296                 } else {
297                     newAliases.add(m);
298                 }
299             }
300             this.aliases = newAliases;
301             for (Iterator i = members.iterator(); i.hasNext(); ) {
302                 GraphNode n = (GraphNode)i.next();
303                 if (n != this) {
304                     pred.addAll(n.pred);
305                     n.relocateAllRefTo(this, done);
306                     n.aliases = this;
307                 }
308             }
309             pred.removeAll(members);
310         }
311         
312         /**
313          * This node is being absorbed into an SCC with the given node as the
314          * new lead node. Trace out all predecessors to this node and relocate
315          * them to point to the new lead node.
316          */

317         private void relocateAllRefTo(GraphNode lead, Set done) {
318             visitPredecessors(new Visitor(){
319                 public void visit(GraphNode node, Object JavaDoc done, Object JavaDoc leadIn) {
320                     if (((Set)done).add(node)) {
321                         GraphNode lead = (GraphNode)leadIn;
322                         Set members = (Set)lead.aliases;
323                         int before = node.succ.size();
324                         node.succ.removeAll(members);
325                         node.succClosed.removeAll(members);
326                         node.succClosed.add(lead);
327                         if (node.succ.size() != before) {
328                             node.succ.add(lead);
329                         }
330                     }
331                 }
332             }, done, lead);
333         }
334         
335         /**
336          * Return an iterator over all of the triples representing outgoing links
337          * from this node.
338          * @param closed if set to true it returns triples in the transitive closure,
339          * if set to false it returns triples in the transitive reduction
340          * @param cache the enclosing TransitiveGraphCache
341          */

342         public ExtendedIterator listTriples(boolean closed, TransitiveGraphCache tgc) {
343             if (tgc.cacheTriples) {
344                 // TODO implement - for now default to non-cached
345
return WrappedIterator.create(leadNode().triplesForSuccessors(rdfNode, closed, tgc).iterator());
346             } else {
347                 return WrappedIterator.create(leadNode().triplesForSuccessors(rdfNode, closed, tgc).iterator());
348             }
349         }
350         
351         /**
352          * Create a list of triples for a given set of successors to this node.
353          */

354         private List triplesForSuccessors(Node base, boolean closed, TransitiveGraphCache tgc) {
355             Set successors = closed ? succClosed : succ;
356             ArrayList result = new ArrayList(successors.size() + 10);
357             result.add(new Triple(base, tgc.closedPredicate, base)); // implicit reflexive case
358
for (Iterator i = successors.iterator(); i.hasNext(); ) {
359                 GraphNode s = (GraphNode)i.next();
360                 result.add(new Triple(base, tgc.closedPredicate, s.rdfNode));
361                 if (s.aliases instanceof Set) {
362                     for (Iterator j = ((Set)s.aliases).iterator(); j.hasNext(); ) {
363                         result.add(new Triple(base, tgc.closedPredicate, ((GraphNode)j.next()).rdfNode));
364                     }
365                 }
366             }
367             if (aliases instanceof Set) {
368                 for (Iterator j = ((Set)aliases).iterator(); j.hasNext(); ) {
369                     result.add(new Triple(base, tgc.closedPredicate, ((GraphNode)j.next()).rdfNode));
370                 }
371             }
372             return result;
373         }
374         
375         /**
376          * Return an iterator over all of the triples representing incoming links to this node.
377          * Currently no caching enabled.
378          */

379         public ExtendedIterator listPredecessorTriples(boolean closed, TransitiveGraphCache tgc) {
380             return new GraphWalker(leadNode(), rdfNode, closed, tgc.closedPredicate);
381         }
382         
383         /**
384          * Print node label to assist with debug.
385          */

386         public String JavaDoc toString() {
387             return "[" + rdfNode.getLocalName() + "]";
388         }
389         
390         /**
391          * Full dump for debugging
392          */

393         public String JavaDoc dump() {
394             String JavaDoc result = rdfNode.getLocalName();
395             if (aliases != null) {
396                 if (aliases instanceof GraphNode) {
397                     result = result + " leader=" + aliases + ", ";
398                 } else {
399                     result = result + " SCC=" + dumpSet((Set)aliases) +", ";
400                 }
401             }
402             return result + " succ=" + dumpSet(succ) + ", succClose=" + dumpSet(succClosed) + ", pred=" + dumpSet(pred);
403         }
404         
405         /**
406          * Dump a set to a string for debug.
407          */

408         private String JavaDoc dumpSet(Set s) {
409             StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
410             sb.append("{");
411             boolean started = false;
412             for (Iterator i = s.iterator(); i.hasNext(); ) {
413                 if (started) {
414                     sb.append(", ");
415                 } else {
416                     started = true;
417                 }
418                 sb.append(i.next().toString());
419             }
420             sb.append("}");
421             return sb.toString();
422         }
423         
424     } // End of GraphNode inner class
425

426     /**
427      * Inner class used to walk backward links of the graph.
428      * <p> The triples are dynamically allocated which is costly.
429      */

430     private static class GraphWalker extends NiceIterator implements ExtendedIterator {
431         
432         /** Indicate if this is a shallow or deep walk */
433         boolean isDeep;
434         
435         /** The current node being visited */
436         GraphNode node;
437         
438         /** The root node for reconstructing triples */
439         Node root;
440         
441         /** The predicate for reconstructing triples */
442         Node predicate;
443         
444         /** Iterator over the predecessors to the current node bein walked */
445         Iterator iterator = null;
446         
447         /** Iterator over the aliases of the current predecessor being output */
448         Iterator aliasIterator = null;
449         
450         /** stack of graph nodes being walked */
451         ArrayList nodeStack = new ArrayList();
452         
453         /** stack of iterators for the higher nodes in the walk */
454         ArrayList iteratorStack = new ArrayList();
455         
456         /** The next value to be returned */
457         Triple next;
458         
459         /** The set of junction nodes already visited */
460         HashSet visited = new HashSet();
461         
462         /**
463          * Constructor. Creates an iterator which will walk
464          * the graph, returning triples.
465          * @param node the starting node for the walk
466          * @param rdfNode the rdfNode we are try to find predecessors for
467          * @param closed set to true of walking the whole transitive closure
468          * @param predicate the predicate to be walked
469          */

470         GraphWalker(GraphNode node, Node rdfNode, boolean closed, Node predicate) {
471             isDeep = closed;
472             this.node = node;
473             this.root = rdfNode;
474             this.predicate = predicate;
475             this.iterator = node.pred.iterator();
476             if (node.aliases instanceof Set) {
477                 aliasIterator = ((Set)node.aliases).iterator();
478             }
479             next = new Triple(root, predicate, root); // implicit reflexive case
480
}
481         
482         /** Iterator interface - test if more values available */
483         public boolean hasNext() {
484             return next != null;
485         }
486         
487         /** Iterator interface - get next value */
488         public Object JavaDoc next() {
489             Object JavaDoc toReturn = next;
490             walkOne();
491             return toReturn;
492         }
493                 
494         /**
495          * Walk one step
496          */

497         protected void walkOne() {
498             if (aliasIterator != null) {
499                 if (aliasIterator.hasNext()) {
500                     GraphNode nextNode = (GraphNode)aliasIterator.next();
501                     next = new Triple(nextNode.rdfNode, predicate, root);
502                     return;
503                 } else {
504                     aliasIterator = null;
505                 }
506             }
507             if (iterator.hasNext()) {
508                 GraphNode nextNode = (GraphNode)iterator.next();
509                 if (visited.add(nextNode)) {
510                     // Set up for depth-first visit next
511
if (isDeep)
512                         pushStack(nextNode);
513                     next = new Triple(nextNode.rdfNode, predicate, root);
514                     if (nextNode.aliases instanceof Set) {
515                         aliasIterator = ((Set)nextNode.aliases).iterator();
516                     }
517                 } else {
518                     // Already visited this junction, skip it
519
walkOne();
520                     return;
521                 }
522             } else {
523                 // Finished this node
524
if (nodeStack.isEmpty()) {
525                     next = null;
526                     return;
527                 }
528                 popStack();
529                 walkOne();
530             }
531         }
532         
533         /**
534          * Push the current state onto the stack
535          */

536         protected void pushStack(GraphNode next) {
537             nodeStack.add(node);
538             iteratorStack.add(iterator);
539             iterator = next.pred.iterator();
540             node = next;
541         }
542         
543         /**
544          * Pop the prior state back onto the stack
545          */

546         protected void popStack() {
547             int i = nodeStack.size()-1;
548             iterator = (Iterator) iteratorStack.remove(i);
549             node = (GraphNode) nodeStack.remove(i);
550         }
551         
552     } // End of GraphWalker inner class
553

554     /**
555      * Inner class used to do a complete walk over the graph
556      */

557     private static class FullGraphWalker extends NiceIterator implements ExtendedIterator {
558
559         /** Flag whether we are walking over the closed or direct relations */
560         boolean closed;
561         
562         /** Iterator over the start nodes in the node map */
563         Iterator baseNodeIt;
564         
565         /** The current node being visited */
566         GraphNode node;
567         
568         /** The root node for reconstructing triples */
569         Node nodeN;
570         
571         /** The predicate for reconstructing triples */
572         Node predicate;
573         
574         /** Iterator over the successor nodes for the baseNode */
575         Iterator succIt = null;
576         
577         /** Iterator over the aliases for the current successor */
578         Iterator aliasesIt = null;
579         
580         /** The next value to be returned */
581         Triple next;
582         
583         /** Construct a walker for the full closed or direct graph */
584         FullGraphWalker(boolean closed, Node predicate, HashMap nodes) {
585             this.predicate = predicate;
586             this.closed = closed;
587             baseNodeIt = nodes.values().iterator();
588             walkOne();
589         }
590         
591         /** Iterator interface - test if more values available */
592         public boolean hasNext() {
593             return next != null;
594         }
595         
596         /** Iterator interface - get next value */
597         public Object JavaDoc next() {
598             Object JavaDoc toReturn = next;
599             walkOne();
600             return toReturn;
601         }
602                 
603         /**
604          * Walk one step
605          */

606         protected void walkOne() {
607             if (aliasesIt != null) {
608                 if (aliasesIt.hasNext()) {
609                     next = new Triple(nodeN, predicate, ((GraphNode)aliasesIt.next()).rdfNode);
610                     return;
611                 } else {
612                     aliasesIt = null; // End of aliases
613
}
614             }
615             
616             if (succIt != null) {
617                 if (succIt.hasNext()) {
618                     GraphNode succ = (GraphNode)succIt.next();
619                     if (succ.aliases instanceof Set) {
620                         aliasesIt = ((Set)succ.aliases).iterator();
621                     }
622                     next = new Triple(nodeN, predicate, succ.rdfNode);
623                     return;
624                 } else {
625                     succIt = null; // End of the successors
626
}
627             }
628             
629             if (baseNodeIt.hasNext()) {
630                 node = (GraphNode)baseNodeIt.next();
631                 nodeN = node.rdfNode;
632                 succIt = (closed ? node.succClosed : node.succ).iterator();
633                 next = new Triple(nodeN, predicate, nodeN); // Implicit reflexive case
634
} else {
635                 next = null; // End of walk
636
}
637         }
638         
639     } // End of FullGraphWalker inner class
640

641     /**
642      * Constructor - create a new cache to hold the given relation information.
643      * @param directPredicate The RDF predicate representing the direct relation
644      * @param closedPredicate The RDF predicate representing the closed relation
645      */

646     public TransitiveGraphCache(Node directPredicate, Node closedPredicate) {
647         this.directPredicate = directPredicate;
648         this.closedPredicate = closedPredicate;
649     }
650     
651     /**
652      * Returns the closedPredicate.
653      * @return Node
654      */

655     public Node getClosedPredicate() {
656         return closedPredicate;
657     }
658
659     /**
660      * Returns the directPredicate.
661      * @return Node
662      */

663     public Node getDirectPredicate() {
664         return directPredicate;
665     }
666      
667     /**
668      * Register a new relation instance in the cache
669      */

670     public synchronized void addRelation(Triple t) {
671         originalTriples.add(t);
672         addRelation(t.getSubject(), t.getObject());
673     }
674     
675     /**
676      * Register a new relation instance in the cache
677      */

678     private void addRelation(Node start, Node end) {
679         if (start.equals(end)) return; // Reflexive case is built in
680
GraphNode startN = getLead(start);
681         GraphNode endN = getLead(end);
682         
683         // Check if this link is already known about
684
if (startN.pathTo(endN)) {
685             // yes, so no work to do
686
return;
687         }
688
689         boolean needJoin = endN.pathTo(startN);
690         Set members = null;
691         if (needJoin) {
692             // Reduce graph to DAG by factoring out SCCs
693
// startN.assertLinkTo(endN);
694
// First find all the members of the new component
695
members = new HashSet();
696             members.add(endN);
697             startN.visitPredecessors(new Visitor() {
698                 public void visit(GraphNode node, Object JavaDoc members, Object JavaDoc endN) {
699                     if (((GraphNode)endN).pathTo(node)) ((Set)members).add(node);
700                 } }, members, endN);
701             // Then create the SCC
702
startN.makeLeadNodeFor(members);
703             // Now propagate the closure in the normalized graph
704
startN.propagateSCC();
705         } else {
706             // Walk all predecessors of start retracting redundant direct links
707
// and adding missing closed links
708
startN.propagateAdd(endN);
709             startN.assertLinkTo(endN);
710         }
711         
712         if (needJoin) {
713             // Create a new strongly connected component
714
}
715     }
716     
717     /**
718      * Remove an instance of a relation from the cache.
719      */

720     public void removeRelation(Triple t) {
721         Node start = t.getSubject();
722         Node end = t.getObject();
723         if (start == end) {
724             return; // Reflexive case is built in
725
}
726         GraphNode startN = getLead(start);
727         GraphNode endN = getLead(end);
728         if (startN != endN && !(startN.directPathTo(endN))) {
729             // indirect link can't be removed by itself
730
return;
731         }
732         // This is a remove of a direct link possibly within an SCC
733
// Delay as long as possible and do deletes in a batch
734
if (deletesPending == null) {
735             deletesPending = new HashSet();
736         }
737         deletesPending.add(t);
738     }
739
740     /**
741      * Process outstanding delete actions
742      */

743     private void processDeletes() {
744         // The kernel is the set of start nodes of deleted links
745
Set kernel = new HashSet();
746         for (Iterator i = deletesPending.iterator(); i.hasNext(); ) {
747             Triple t = (Triple)i.next();
748             GraphNode start = (GraphNode)nodeMap.get(t.getSubject());
749             kernel.add(start);
750         }
751         
752         // The predecessor set of kernel
753
Set pKernel = new HashSet();
754         pKernel.addAll(kernel);
755         for (Iterator i = nodeMap.values().iterator(); i.hasNext(); ) {
756             GraphNode n = (GraphNode)i.next();
757             for (Iterator j = kernel.iterator(); j.hasNext();) {
758                 GraphNode target = (GraphNode)j.next();
759                 if (n.pathTo(target)) {
760                     pKernel.add(n); break;
761                 }
762             }
763         }
764         
765         // Cut the pKernel away from the finge of nodes that it connects to
766
for (Iterator i = pKernel.iterator(); i.hasNext(); ) {
767             GraphNode n = (GraphNode)i.next();
768             for (Iterator j = n.succ.iterator(); j.hasNext(); ) {
769                 GraphNode fringe = (GraphNode)j.next();
770                 if (! pKernel.contains(fringe)) {
771                     fringe.pred.remove(n);
772                 }
773             }
774             n.succ.clear();
775             n.succClosed.clear();
776             n.pred.clear();
777         }
778         
779         // Delete the triples
780
originalTriples.removeAll(deletesPending);
781         deletesPending.clear();
782         
783         // Reinsert the remaining links
784
for (Iterator i = originalTriples.iterator(); i.hasNext(); ) {
785             Triple t = (Triple)i.next();
786             GraphNode n = (GraphNode)nodeMap.get(t.getSubject());
787             if (pKernel.contains(n)) {
788                 addRelation(t);
789             }
790         }
791     }
792     
793     /**
794      * Extended find interface used in situations where the implementator
795      * may or may not be able to answer the complete query.
796      * <p>
797      * In this case any query on the direct or closed predicates will
798      * be assumed complete, any other query will pass on to the continuation.</p>
799      * @param pattern a TriplePattern to be matched against the data
800      * @param continuation either a Finder or a normal Graph which
801      * will be asked for additional match results if the implementor
802      * may not have completely satisfied the query.
803      */

804     public ExtendedIterator findWithContinuation(TriplePattern pattern, Finder continuation) {
805         Node p = pattern.getPredicate();
806         
807         if (p.isVariable()) {
808             // wildcard predicate so return merge of cache and continuation
809
return find(pattern).andThen(continuation.find(pattern));
810         } else if (p.equals(directPredicate) || p.equals(closedPredicate)) {
811             // Satisfy entire query from the cache
812
return find(pattern);
813         } else {
814             // No matching triples in this cache so just search the continuation
815
return continuation.find(pattern);
816         }
817         
818     }
819     
820     /**
821      * Return true if the given pattern occurs somewhere in the find sequence.
822      */

823     public boolean contains(TriplePattern pattern) {
824         ClosableIterator it = find(pattern);
825         boolean result = it.hasNext();
826         it.close();
827         return result;
828     }
829     /**
830      * Return an iterator over all registered subject nodes
831      */

832     public ExtendedIterator listAllSubjects() {
833         return WrappedIterator.create(nodeMap.keySet().iterator());
834     }
835    
836     /**
837      * Return true if the given Node is registered as a subject node
838      */

839     public boolean isSubject(Node node) {
840         return nodeMap.keySet().contains(node);
841     }
842     
843     /**
844      * Cache all instances of the given predicate which are
845      * present in the given Graph.
846      * @param graph the searchable set of triples to cache
847      * @param predicate the predicate to cache, need not be the registered
848      * predicate due to subProperty declarations
849      * @return returns true if new information has been cached
850      */

851     public boolean cacheAll(Finder graph, Node predicate) {
852         ExtendedIterator it = graph.find(new TriplePattern(null, predicate, null));
853         boolean foundsome = it.hasNext();
854         while (it.hasNext()) {
855             Triple triple = (Triple) it.next();
856             addRelation(triple);
857         }
858         it.close();
859         return foundsome;
860     }
861     
862     /**
863      * Basic pattern lookup interface.
864      * @param pattern a TriplePattern to be matched against the data
865      * @return a ExtendedIterator over all Triples in the data set
866      * that match the pattern
867      */

868     public ExtendedIterator find(TriplePattern pattern) {
869         if (deletesPending != null && deletesPending.size() > 0) {
870             processDeletes();
871         }
872
873         Node s = pattern.getSubject();
874         Node p = pattern.getPredicate();
875         Node o = pattern.getObject();
876         
877         if (p.isVariable() || p.equals(directPredicate) || p.equals(closedPredicate)) {
878             boolean closed = !p.equals(directPredicate);
879             Node pred = closedPredicate; // p.isVariable() ? closedPredicate : p;
880
if (s.isVariable()) {
881                 if (o.isVariable()) {
882                     // list all the graph contents
883
// ExtendedIterator result = null;
884
// for (Iterator i = nodeMap.values().iterator(); i.hasNext(); ) {
885
// ExtendedIterator nexti = ((GraphNode)i.next()).listTriples(closed, this);
886
// if (result == null) {
887
// result = nexti;
888
// } else {
889
// result = result.andThen(nexti);
890
// }
891
// }
892
// if (result == null) {
893
// return NullIterator.instance;
894
// }
895
return new FullGraphWalker(closed, closedPredicate, nodeMap);
896                 } else {
897                     // list all backwards from o
898
GraphNode gn_o = (GraphNode)nodeMap.get(o);
899                     if (gn_o == null) return NullIterator.instance;
900                     return gn_o.listPredecessorTriples(closed, this);
901                 }
902             } else {
903                 GraphNode gn_s = (GraphNode)nodeMap.get(s);
904                 if (gn_s == null) return NullIterator.instance;
905                 if (o.isVariable()) {
906                     // list forward from s
907
return gn_s.listTriples(closed, this);
908                 } else {
909                     // Singleton test
910
GraphNode gn_o = (GraphNode)nodeMap.get(o);
911                     gn_s = gn_s.leadNode();
912                     if (gn_o == null) return NullIterator.instance;
913                     gn_o = gn_o.leadNode();
914                     if ( closed ? gn_s.pathTo(gn_o) : gn_s.directPathTo(gn_o) ) {
915                         return new SingletonIterator(new Triple(s, pred, o));
916                     } else {
917                         return NullIterator.instance;
918                     }
919                 }
920             }
921         } else {
922             // No matching triples in this cache
923
return NullIterator.instance;
924         }
925     }
926     
927     /**
928      * Create a deep copy of the cache contents.
929      * Works by creating a completely new cache and just adding in the
930      * direct links.
931      */

932     public TransitiveGraphCache deepCopy() {
933         TransitiveGraphCache copy = new TransitiveGraphCache(directPredicate, closedPredicate);
934         Iterator i = find(new TriplePattern(null, directPredicate, null));
935         while (i.hasNext()) {
936             Triple t = (Triple)i.next();
937             copy.addRelation(t.getSubject(), t.getObject());
938         }
939         return copy;
940     }
941     
942     /**
943      * Clear the entire cache contents.
944      */

945     public void clear() {
946         nodeMap.clear();
947     }
948     
949     /**
950      * Enable/disabling caching of the Triples representing the relationships. If this is
951      * enabled then a number of triples quadratic in the graph depth will be stored. If it
952      * is disabled then all queries will turn over storage dynamically creating the result triples.
953      */

954     public void setCaching(boolean enable) {
955         if (! enable && cacheTriples) {
956             // Switching off so clear the existing cache
957
for (Iterator i = nodeMap.values().iterator(); i.hasNext(); ) {
958                 ((GraphNode)i.next()).clearTripleCache();
959             }
960         }
961         cacheTriples = enable;
962     }
963     
964     /**
965      * Dump a description of the cache to a string for debug.
966      */

967     public String JavaDoc dump() {
968         StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
969         for (Iterator i = nodeMap.values().iterator(); i.hasNext(); ) {
970             GraphNode n = (GraphNode)i.next();
971             sb.append(n.dump());
972             sb.append("\n");
973         }
974         return sb.toString();
975     }
976     
977 // ----------------------------------------------------------------------
978
// Internal utility methods
979
// ----------------------------------------------------------------------
980

981     /**
982      * Return the lead node of the strongly connected component corresponding
983      * to the given RDF node.
984      */

985     private GraphNode getLead(Node n) {
986         GraphNode gn = (GraphNode)nodeMap.get(n);
987         if (gn == null) {
988             gn = new GraphNode(n);
989             nodeMap.put(n, gn);
990             return gn;
991         } else {
992             return gn.leadNode();
993         }
994     }
995     
996 }
997
998
999 /*
1000    (c) Copyright 2004, 2005 Hewlett-Packard Development Company, LP
1001    All rights reserved.
1002
1003    Redistribution and use in source and binary forms, with or without
1004    modification, are permitted provided that the following conditions
1005    are met:
1006
1007    1. Redistributions of source code must retain the above copyright
1008       notice, this list of conditions and the following disclaimer.
1009
1010    2. Redistributions in binary form must reproduce the above copyright
1011       notice, this list of conditions and the following disclaimer in the
1012       documentation and/or other materials provided with the distribution.
1013
1014    3. The name of the author may not be used to endorse or promote products
1015       derived from this software without specific prior written permission.
1016
1017    THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1018    IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1019    OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1020    IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1021    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
1022    NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
1023    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
1024    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
1025    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
1026    THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1027*/

1028
Popular Tags