[Mulgara-svn] r1389 - in branches/xa11: conf src/jar/resolver-spi/java/org/mulgara/store/statement src/jar/resolver-store/java/org/mulgara/resolver/store src/jar/resolver-store/java/org/mulgara/store/statement src/jar/resolver-store/java/org/mulgara/store/statement/xa src/jar/resolver-store/java/org/mulgara/store/statement/xa11

pag at mulgara.org pag at mulgara.org
Tue Dec 2 20:08:37 UTC 2008


Author: pag
Date: 2008-12-02 12:08:36 -0800 (Tue, 02 Dec 2008)
New Revision: 1389

Added:
   branches/xa11/src/jar/resolver-store/java/org/mulgara/resolver/store/XA11StatementStoreResolverFactory.java
   branches/xa11/src/jar/resolver-store/java/org/mulgara/store/statement/xa11/
   branches/xa11/src/jar/resolver-store/java/org/mulgara/store/statement/xa11/XA11StatementStoreImpl.java
   branches/xa11/src/jar/resolver-store/java/org/mulgara/store/statement/xa11/XA11StatementStoreImplUnitTest.java
Modified:
   branches/xa11/conf/mulgara-config.xml
   branches/xa11/src/jar/resolver-spi/java/org/mulgara/store/statement/StatementStoreAbstractUnitTest.java
   branches/xa11/src/jar/resolver-store/java/org/mulgara/resolver/store/StatementStoreResolverFactory.java
   branches/xa11/src/jar/resolver-store/java/org/mulgara/store/statement/xa/TripleAVLFile.java
Log:
Re-implemented r1380 in this new branch

Modified: branches/xa11/conf/mulgara-config.xml
===================================================================
--- branches/xa11/conf/mulgara-config.xml	2008-12-02 18:59:07 UTC (rev 1388)
+++ branches/xa11/conf/mulgara-config.xml	2008-12-02 20:08:36 UTC (rev 1389)
@@ -92,7 +92,7 @@
   <TemporaryNodePoolFactory  type="org.mulgara.store.nodepool.memory.MemoryNodePoolFactory"/>
   <PersistentStringPoolFactory type="org.mulgara.store.stringpool.xa11.XA11StringPoolFactory" dir="xaStringPool"/>
   <TemporaryStringPoolFactory type="org.mulgara.store.stringpool.memory.MemoryStringPoolFactory"/>
-  <PersistentResolverFactory type="org.mulgara.resolver.store.StatementStoreResolverFactory" dir="xaStatementStore"/>
+  <PersistentResolverFactory type="org.mulgara.resolver.store.XA11StatementStoreResolverFactory" dir="xaStatementStore"/>
   <TemporaryResolverFactory type="org.mulgara.resolver.memory.MemoryResolverFactory" dir="tempStatementStore"/>
 
   <!--

Modified: branches/xa11/src/jar/resolver-spi/java/org/mulgara/store/statement/StatementStoreAbstractUnitTest.java
===================================================================
--- branches/xa11/src/jar/resolver-spi/java/org/mulgara/store/statement/StatementStoreAbstractUnitTest.java	2008-12-02 18:59:07 UTC (rev 1388)
+++ branches/xa11/src/jar/resolver-spi/java/org/mulgara/store/statement/StatementStoreAbstractUnitTest.java	2008-12-02 20:08:36 UTC (rev 1389)
@@ -27,10 +27,9 @@
 
 package org.mulgara.store.statement;
 
-// Java 2 standard packages
-import java.util.*;
-
 // third party packages
+import java.util.Arrays;
+
 import junit.framework.*;
 import org.apache.log4j.Logger;
 
@@ -39,6 +38,7 @@
 import org.mulgara.store.nodepool.NodePool;
 import org.mulgara.store.tuples.TestTuples;
 import org.mulgara.store.tuples.Tuples;
+import org.mulgara.store.tuples.TuplesOperations;
 import org.mulgara.store.xa.XAStatementStore;
 
 /**
@@ -100,8 +100,6 @@
 
   /**
    * Test {@link StatementStore#isEmpty}.
-   *
-   * @throws Exception EXCEPTION TO DO
    */
   public void testIsEmpty() throws Exception {
 
@@ -119,17 +117,13 @@
       store.removeTriples(1, 2, 3, 9);
 
       assertTrue(store.isEmpty());
-    }
-    catch (UnsupportedOperationException e) {
-
+    } catch (UnsupportedOperationException e) {
       log.warn("IsEmpty method unsupported", e);
     }
   }
 
   /**
    * Test {@link StatementStore#existsTriples}.
-   *
-   * @throws Exception EXCEPTION TO DO
    */
   public void testExists() throws Exception {
 
@@ -137,63 +131,60 @@
 
       long n = NodePool.NONE;
 
-      assertTrue(store.existsTriples(1, 2, 3, n));
-      assertTrue(store.existsTriples(1, 2, 4, n));
-      assertTrue(store.existsTriples(2, 5, 6, n));
-      assertTrue(!store.existsTriples(1, 3, 2, n));
-      assertTrue(!store.existsTriples(9, 9, 9, n));
+      assertTrue(store.existsTriples(n, 2, 3, 1));
+      assertTrue(store.existsTriples(n, 2, 4, 2));
+      assertTrue(store.existsTriples(n, 5, 6, 2));
+      assertTrue(!store.existsTriples(n, 3, 2, 1));
+      assertTrue(!store.existsTriples(n, 9, 9, 9));
 
-      assertTrue(store.existsTriples(1, 2, n, n));
-      assertTrue(store.existsTriples(2, 5, n, n));
-      assertTrue(!store.existsTriples(1, 3, n, n));
-      assertTrue(!store.existsTriples(2, 9, n, n));
+      assertTrue(store.existsTriples(1, n, 3, 1));
+      assertTrue(store.existsTriples(2, n, 6, 2));
+      assertTrue(!store.existsTriples(2, n, 4, 2));
+      assertTrue(!store.existsTriples(9, n, 3, 1));
 
-      assertTrue(store.existsTriples(n, 2, 3, n));
-      assertTrue(store.existsTriples(n, 2, 4, n));
-      assertTrue(!store.existsTriples(n, 1, 3, n));
-      assertTrue(!store.existsTriples(n, 2, 9, n));
+      assertTrue(store.existsTriples(n, n, 3, 1));
+      assertTrue(store.existsTriples(n, n, 4, 2));
+      assertTrue(!store.existsTriples(n, n, 3, 2));
+      assertTrue(!store.existsTriples(n, n, 9, 2));
 
-      assertTrue(store.existsTriples(1, n, 3, n));
-      assertTrue(store.existsTriples(2, n, 6, n));
-      assertTrue(!store.existsTriples(2, n, 4, n));
-      assertTrue(!store.existsTriples(9, n, 3, n));
+      assertTrue(store.existsTriples(1, 2, n, 1));
+      assertTrue(store.existsTriples(2, 5, n, 2));
+      assertTrue(!store.existsTriples(1, 3, n, 1));
+      assertTrue(!store.existsTriples(2, 9, n, 2));
 
-      assertTrue(store.existsTriples(1, n, n, n));
-      assertTrue(store.existsTriples(2, n, n, n));
-      assertTrue(!store.existsTriples(3, n, n, n));
+      assertTrue(store.existsTriples(n, 2, n, 1));
+      assertTrue(store.existsTriples(n, 5, n, 2));
+      assertTrue(!store.existsTriples(n, 3, n, 1));
+      assertTrue(!store.existsTriples(n, 9, n, 2));
 
-      assertTrue(store.existsTriples(n, 2, n, n));
-      assertTrue(store.existsTriples(n, 5, n, n));
-      assertTrue(!store.existsTriples(n, 1, n, n));
+      assertTrue(store.existsTriples(1, n, n, 1));
+      assertTrue(store.existsTriples(1, n, n, 2));
+      assertTrue(store.existsTriples(2, n, n, 2));
+      assertTrue(!store.existsTriples(3, n, n, 2));
 
-      assertTrue(store.existsTriples(n, n, 3, n));
-      assertTrue(store.existsTriples(n, n, 6, n));
-      assertTrue(!store.existsTriples(n, n, 5, n));
-    }
-    catch (UnsupportedOperationException e) {
-
+      assertTrue(store.existsTriples(n, n, n, 1));
+      assertTrue(store.existsTriples(n, n, n, 2));
+      assertTrue(!store.existsTriples(n, n, n, 3));
+    } catch (UnsupportedOperationException e) {
       log.warn("Exists method unsupported", e);
     }
   }
 
   /**
    * Test {@link StatementStore#findTuples}.
-   *
-   * @throws Exception EXCEPTION TO DO
    */
   public void testDump() throws Exception {
 
     TestTuples expected = new TestTuples();
-    add(expected, store.VARIABLES, new long[] {
-        1, 2, 3, 1});
-    add(expected, store.VARIABLES, new long[] {
-        1, 2, 4, 2});
-    add(expected, store.VARIABLES, new long[] {
-        2, 5, 6, 2});
+    add(expected, StatementStore.VARIABLES, new long[] {1, 2, 3, 1});
+    add(expected, StatementStore.VARIABLES, new long[] {1, 2, 4, 2});
+    add(expected, StatementStore.VARIABLES, new long[] {2, 5, 6, 2});
 
     Tuples t = store.findTuples(NodePool.NONE, NodePool.NONE, NodePool.NONE, NodePool.NONE);
-    assertEquals(expected, t);
+    Tuples r = TuplesOperations.project(t, Arrays.asList(StatementStore.VARIABLES));
+    assertEquals(expected, r);
     t.close();
+    r.close();
     expected.close();
   }
 
@@ -217,37 +208,37 @@
     store.removeTriples(2, 5, 6, 2);
     store.removeTriples(1, 2, 4, 2);
 
-    assertTrue(!store.existsTriples(1, 2, 3, NodePool.NONE));
-    assertTrue(!store.existsTriples(1, 2, 4, NodePool.NONE));
-    assertTrue(!store.existsTriples(2, 5, 6, NodePool.NONE));
+    assertTrue(!store.existsTriples(NodePool.NONE, 2, 3, 1));
+    assertTrue(!store.existsTriples(NodePool.NONE, 2, 4, 2));
+    assertTrue(!store.existsTriples(NodePool.NONE, 5, 6, 2));
   }
 
   /**
    * Test {@link StatementStore#findTuples}.
-   *
-   * @throws Exception EXCEPTION TO DO
    */
   public void testFindTriplesByNode0() throws Exception {
 
     TestTuples expected = new TestTuples();
     Variable[] vars =
-        new Variable[] {
-        store.VARIABLES[1], store.VARIABLES[2], store.VARIABLES[3]};
-    add(expected, vars, new long[] {
-        2, 3, 1});
-    add(expected, vars, new long[] {
-        2, 4, 2});
+        new Variable[] {StatementStore.VARIABLES[1], StatementStore.VARIABLES[2], StatementStore.VARIABLES[3]};
+    add(expected, vars, new long[] {2, 3, 1});
+    add(expected, vars, new long[] {2, 4, 2});
 
-    Tuples t = store.findTuples(1, NodePool.NONE, NodePool.NONE, NodePool.NONE);
-    assertEquals(expected, t);
-    t.close();
-    expected.close();
+    try {
+      Tuples t = store.findTuples(1, NodePool.NONE, NodePool.NONE, NodePool.NONE);
+      assertEquals(expected, t);
+      t.close();
+    } catch (IllegalArgumentException e) {
+      // not supported
+      return;
+    } finally {
+      expected.close();
+    }
 
     expected = new TestTuples();
-    add(expected, vars, new long[] {
-        5, 6, 2});
+    add(expected, vars, new long[] {5, 6, 2});
 
-    t = store.findTuples(2, NodePool.NONE, NodePool.NONE, NodePool.NONE);
+    Tuples t = store.findTuples(2, NodePool.NONE, NodePool.NONE, NodePool.NONE);
     assertEquals(expected, t);
     t.close();
     expected.close();
@@ -261,17 +252,17 @@
   public void testFindTriplesByNode1() throws Exception {
 
     TestTuples expected = new TestTuples();
-    Variable[] vars =
-        new Variable[] {
-        store.VARIABLES[2], store.VARIABLES[0], store.VARIABLES[3]};
-    add(expected, vars, new long[] {
-        3, 1, 1});
-    add(expected, vars, new long[] {
-        4, 1, 2});
+    Variable[] vars = new Variable[] {StatementStore.VARIABLES[2], StatementStore.VARIABLES[0], StatementStore.VARIABLES[3]};
+    add(expected, vars, new long[] {3, 1, 1});
+    add(expected, vars, new long[] {4, 1, 2});
 
-    Tuples t = store.findTuples(NodePool.NONE, 2, NodePool.NONE, NodePool.NONE);
-    assertEquals(expected, t);
-    t.close();
+    try {
+      Tuples t = store.findTuples(NodePool.NONE, 2, NodePool.NONE, NodePool.NONE);
+      assertEquals(expected, t);
+      t.close();
+    } catch (IllegalArgumentException e) {
+      // not supported
+    }
     expected.close();
   }
 
@@ -283,15 +274,16 @@
   public void testFindTriplesByNode2() throws Exception {
 
     TestTuples expected = new TestTuples();
-    Variable[] vars =
-        new Variable[] {
-        store.VARIABLES[0], store.VARIABLES[1], store.VARIABLES[3]};
-    add(expected, vars, new long[] {
-        1, 2, 1});
+    Variable[] vars = new Variable[] {StatementStore.VARIABLES[0], StatementStore.VARIABLES[1], StatementStore.VARIABLES[3]};
+    add(expected, vars, new long[] {1, 2, 1});
 
-    Tuples t = store.findTuples(NodePool.NONE, NodePool.NONE, 3, NodePool.NONE);
-    assertEquals(expected, t);
-    t.close();
+    try {
+      Tuples t = store.findTuples(NodePool.NONE, NodePool.NONE, 3, NodePool.NONE);
+      assertEquals(expected, t);
+      t.close();
+    } catch (IllegalArgumentException e) {
+      // not supported
+    }
     expected.close();
   }
 
@@ -303,13 +295,9 @@
   public void testFindTriplesByNode3() throws Exception {
 
     TestTuples expected = new TestTuples();
-    Variable[] vars =
-        new Variable[] {
-        store.VARIABLES[0], store.VARIABLES[1], store.VARIABLES[2]};
-    add(expected, vars, new long[] {
-        1, 2, 4});
-    add(expected, vars, new long[] {
-        2, 5, 6});
+    Variable[] vars = new Variable[] {StatementStore.VARIABLES[0], StatementStore.VARIABLES[1], StatementStore.VARIABLES[2]};
+    add(expected, vars, new long[] {1, 2, 4});
+    add(expected, vars, new long[] {2, 5, 6});
 
     Tuples t = store.findTuples(NodePool.NONE, NodePool.NONE, NodePool.NONE, 2);
     assertEquals(expected, t);
@@ -319,64 +307,59 @@
 
   /**
    * Test {@link StatementStore#findTuples}.
-   *
-   * @throws Exception EXCEPTION TO DO
    */
   public void testFindTriplesByNode01() throws Exception {
 
     TestTuples expected = new TestTuples();
-    Variable[] vars = new Variable[] {
-        store.VARIABLES[2], store.VARIABLES[3]};
-    add(expected, vars, new long[] {
-        3, 1});
-    add(expected, vars, new long[] {
-        4, 2});
+    Variable[] vars = new Variable[] {StatementStore.VARIABLES[2], StatementStore.VARIABLES[3]};
+    add(expected, vars, new long[] {3, 1});
+    add(expected, vars, new long[] {4, 2});
 
-    Tuples t = store.findTuples(1, 2, NodePool.NONE, NodePool.NONE);
-    assertEquals(expected, t);
-    t.close();
-
-    t = store.findTuples(1, 3, NodePool.NONE, NodePool.NONE);
-    assertTrue(!expected.equals(t));
-    t.close();
+    try {
+      Tuples t = store.findTuples(1, 2, NodePool.NONE, NodePool.NONE);
+      assertEquals(expected, t);
+      t.close();
+  
+      t = store.findTuples(1, 3, NodePool.NONE, NodePool.NONE);
+      assertTrue(!expected.equals(t));
+      t.close();
+    } catch (IllegalArgumentException e) {
+      // not supported
+    }
     expected.close();
   }
 
   /**
    * Test {@link StatementStore#findTuples}.
-   *
-   * @throws Exception EXCEPTION TO DO
    */
   public void testFindTriplesByNode02() throws Exception {
 
     TestTuples expected = new TestTuples();
-    Variable[] vars = new Variable[] {
-        store.VARIABLES[1], store.VARIABLES[3]};
-    add(expected, vars, new long[] {
-        2, 1});
+    Variable[] vars = new Variable[] {StatementStore.VARIABLES[1], StatementStore.VARIABLES[3]};
+    add(expected, vars, new long[] {2, 1});
 
-    Tuples t = store.findTuples(1, NodePool.NONE, 3, NodePool.NONE);
-    assertEquals(expected, t);
-    t.close();
-
-    t = store.findTuples(1, NodePool.NONE, 4, NodePool.NONE);
-    assertTrue(!expected.equals(t));
-    t.close();
+    try {
+      Tuples t = store.findTuples(1, NodePool.NONE, 3, NodePool.NONE);
+      assertEquals(expected, t);
+      t.close();
+  
+      t = store.findTuples(1, NodePool.NONE, 4, NodePool.NONE);
+      assertTrue(!expected.equals(t));
+      t.close();
+    } catch (IllegalArgumentException e) {
+      // not supported
+    }
     expected.close();
   }
 
   /**
    * Test {@link StatementStore#findTuples}.
-   *
-   * @throws Exception EXCEPTION TO DO
    */
   public void testFindTriplesByNode03() throws Exception {
 
     TestTuples expected = new TestTuples();
-    Variable[] vars = new Variable[] {
-        store.VARIABLES[1], store.VARIABLES[2]};
-    add(expected, vars, new long[] {
-        2, 4});
+    Variable[] vars = new Variable[] {StatementStore.VARIABLES[1], StatementStore.VARIABLES[2]};
+    add(expected, vars, new long[] {2, 4});
 
     Tuples t = store.findTuples(1, NodePool.NONE, NodePool.NONE, 2);
     assertEquals(expected, t);
@@ -386,39 +369,35 @@
 
   /**
    * Test {@link StatementStore#findTuples}.
-   *
-   * @throws Exception EXCEPTION TO DO
    */
   public void testFindTriplesByNode12() throws Exception {
 
     TestTuples expected = new TestTuples();
-    Variable[] vars = new Variable[] {
-        store.VARIABLES[0], store.VARIABLES[3]};
-    add(expected, vars, new long[] {
-        1, 1});
+    Variable[] vars = new Variable[] {StatementStore.VARIABLES[0], StatementStore.VARIABLES[3]};
+    add(expected, vars, new long[] {1, 1});
 
-    Tuples t = store.findTuples(NodePool.NONE, 2, 3, NodePool.NONE);
-    assertEquals(expected, t);
-    t.close();
-
-    t = store.findTuples(NodePool.NONE, 2, 4, NodePool.NONE);
-    assertTrue(!expected.equals(t));
-    t.close();
+    try {
+      Tuples t = store.findTuples(NodePool.NONE, 2, 3, NodePool.NONE);
+      assertEquals(expected, t);
+      t.close();
+  
+      t = store.findTuples(NodePool.NONE, 2, 4, NodePool.NONE);
+      assertTrue(!expected.equals(t));
+      t.close();
+    } catch (IllegalArgumentException e) {
+      // not supported
+    }
     expected.close();
   }
 
   /**
    * Test {@link StatementStore#findTuples}.
-   *
-   * @throws Exception EXCEPTION TO DO
    */
   public void testFindTriplesByNode13() throws Exception {
 
     TestTuples expected = new TestTuples();
-    Variable[] vars = new Variable[] {
-        store.VARIABLES[2], store.VARIABLES[0]};
-    add(expected, vars, new long[] {
-        4, 1});
+    Variable[] vars = new Variable[] {StatementStore.VARIABLES[2], StatementStore.VARIABLES[0]};
+    add(expected, vars, new long[] {4, 1});
 
     Tuples t = store.findTuples(NodePool.NONE, 2, NodePool.NONE, 2);
     assertEquals(expected, t);
@@ -428,16 +407,12 @@
 
   /**
    * Test {@link StatementStore#findTuples}.
-   *
-   * @throws Exception EXCEPTION TO DO
    */
   public void testFindTriplesByNode23() throws Exception {
 
     TestTuples expected = new TestTuples();
-    Variable[] vars = new Variable[] {
-        store.VARIABLES[0], store.VARIABLES[1]};
-    add(expected, vars, new long[] {
-        2, 5});
+    Variable[] vars = new Variable[] {StatementStore.VARIABLES[0], StatementStore.VARIABLES[1]};
+    add(expected, vars, new long[] {2, 5});
 
     Tuples t = store.findTuples(NodePool.NONE, NodePool.NONE, 6, 2);
     assertEquals(expected, t);
@@ -447,14 +422,11 @@
 
   /**
    * Test {@link StatementStore#findTuples}.
-   *
-   * @throws Exception EXCEPTION TO DO
    */
   public void testFindTriplesByNode013() throws Exception {
 
-    TestTuples expected = new TestTuples(store.VARIABLES[2]);
-    Variable[] vars = new Variable[] {
-        store.VARIABLES[2]};
+    TestTuples expected = new TestTuples(StatementStore.VARIABLES[2]);
+    Variable[] vars = new Variable[] {StatementStore.VARIABLES[2]};
     Tuples t = store.findTuples(2, 6, NodePool.NONE, 1);
     assertEquals(expected, t);
     t.close();
@@ -463,8 +435,7 @@
     assertEquals(expected, t);
     t.close();
 
-    add(expected, vars, new long[] {
-        4});
+    add(expected, vars, new long[] {4});
     t = store.findTuples(1, 2, NodePool.NONE, 2);
     assertEquals(expected, t);
     t.close();
@@ -473,20 +444,16 @@
 
   /**
    * Test {@link StatementStore#findTuples}.
-   *
-   * @throws Exception EXCEPTION TO DO
    */
   public void testFindTriplesByNode023() throws Exception {
 
-    TestTuples expected = new TestTuples(store.VARIABLES[1]);
-    Variable[] vars = new Variable[] {
-        store.VARIABLES[1]};
+    TestTuples expected = new TestTuples(StatementStore.VARIABLES[1]);
+    Variable[] vars = new Variable[] {StatementStore.VARIABLES[1]};
     Tuples t = store.findTuples(1, NodePool.NONE, 3, 4);
     assertEquals(expected, t);
     t.close();
 
-    add(expected, vars, new long[] {
-        5});
+    add(expected, vars, new long[] {5});
     t = store.findTuples(2, NodePool.NONE, 6, 2);
     assertEquals(expected, t);
     t.close();
@@ -495,20 +462,16 @@
 
   /**
    * Test {@link StatementStore#findTuples}.
-   *
-   * @throws Exception EXCEPTION TO DO
    */
   public void testFindTriplesByNode123() throws Exception {
 
-    TestTuples expected = new TestTuples(store.VARIABLES[0]);
-    Variable[] vars = new Variable[] {
-        store.VARIABLES[0]};
+    TestTuples expected = new TestTuples(StatementStore.VARIABLES[0]);
+    Variable[] vars = new Variable[] {StatementStore.VARIABLES[0]};
     Tuples t = store.findTuples(NodePool.NONE, 2, 3, 4);
     assertEquals(expected, t);
     t.close();
 
-    add(expected, vars, new long[] {
-        1});
+    add(expected, vars, new long[] {1});
     t = store.findTuples(NodePool.NONE, 2, 3, 1);
     assertEquals(expected, t);
     t.close();
@@ -517,8 +480,6 @@
 
   /**
    * Populate the test store.
-   *
-   * @throws Exception EXCEPTION TO DO
    */
   protected void setUp() throws Exception {
 
@@ -529,46 +490,31 @@
 
   /**
    * Close the test store.
-   *
-   * @throws Exception EXCEPTION TO DO
    */
   protected void tearDown() throws Exception {
-
     if (store != null) {
-
       try {
-
         store.close();
-      }
-      finally {
-
+      } finally {
         store = null;
       }
     }
   }
 
   /**
-   * METHOD TO DO
-   *
-   * @param tt PARAMETER TO DO
-   * @param vars PARAMETER TO DO
-   * @param nodes PARAMETER TO DO
+   * Add a row to a tuples
+   * @param tt The tuples to add to
+   * @param vars The column names in the tuples
+   * @param nodes The values to bind to
    */
   protected void add(TestTuples tt, Variable[] vars, long[] nodes) {
 
-    if (vars.length != nodes.length) {
+    if (vars.length != nodes.length) throw new AssertionError();
 
-      throw new AssertionError();
-    }
-
     for (int i = 0; i < vars.length; ++i) {
-
       if (i == 0) {
-
         tt.or(vars[i], nodes[i]);
-      }
-      else {
-
+      } else {
         tt.and(vars[i], nodes[i]);
       }
     }

Modified: branches/xa11/src/jar/resolver-store/java/org/mulgara/resolver/store/StatementStoreResolverFactory.java
===================================================================
--- branches/xa11/src/jar/resolver-store/java/org/mulgara/resolver/store/StatementStoreResolverFactory.java	2008-12-02 18:59:07 UTC (rev 1388)
+++ branches/xa11/src/jar/resolver-store/java/org/mulgara/resolver/store/StatementStoreResolverFactory.java	2008-12-02 20:08:36 UTC (rev 1389)
@@ -71,9 +71,9 @@
   private long rdfType;
 
   /** The underlying transactional graph that backs the generated resolvers.  */
-  private final XAStatementStore statementStore;
+  protected final XAStatementStore statementStore;
 
-  private XAResolverSessionFactory resolverSessionFactory;
+  protected XAResolverSessionFactory resolverSessionFactory;
 
   //
   // Constructors
@@ -86,7 +86,7 @@
    * @throws IllegalArgumentException {@inheritDoc}
    * @throws ResolverException {@inheritDoc}
    */
-  private StatementStoreResolverFactory(FactoryInitializer initializer,
+  protected StatementStoreResolverFactory(FactoryInitializer initializer,
       XAResolverSessionFactory resolverSessionFactory) throws
       InitializerException {
     // Validate parameters
@@ -96,7 +96,7 @@
 
     try {
       File filePrefix = new File(initializer.getDirectory(), "xa");
-      statementStore = new XAStatementStoreImpl(filePrefix.toString());
+      statementStore = createStore(filePrefix.toString());
       resolverSessionFactory.registerStatementStore(statementStore);
     } catch (Exception e) {
       throw new InitializerException("Couldn't initialize XA store", e);
@@ -238,4 +238,15 @@
           "Failed to obtain a new ResolverSession", er);
     }
   }
+
+
+  /**
+   * Creates the required type of store
+   * @param filePrefix The base for the files being used for storage.
+   * @return a new instance of an XAStatementStore
+   * @throws IOException Error accessing the filesystem
+   */
+  protected XAStatementStore createStore(String filePrefix) throws IOException {
+    return new XAStatementStoreImpl(filePrefix.toString());
+  }
 }

Added: branches/xa11/src/jar/resolver-store/java/org/mulgara/resolver/store/XA11StatementStoreResolverFactory.java
===================================================================
--- branches/xa11/src/jar/resolver-store/java/org/mulgara/resolver/store/XA11StatementStoreResolverFactory.java	                        (rev 0)
+++ branches/xa11/src/jar/resolver-store/java/org/mulgara/resolver/store/XA11StatementStoreResolverFactory.java	2008-12-02 20:08:36 UTC (rev 1389)
@@ -0,0 +1,64 @@
+/*
+ * The contents of this file are subject to the Open Software License
+ * Version 3.0 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.opensource.org/licenses/osl-3.0.txt
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ */
+
+package org.mulgara.resolver.store;
+
+import java.io.IOException;
+
+import org.mulgara.resolver.spi.FactoryInitializer;
+import org.mulgara.resolver.spi.InitializerException;
+import org.mulgara.resolver.spi.ResolverFactory;
+import org.mulgara.store.statement.xa11.XA11StatementStoreImpl;
+import org.mulgara.store.xa.XAResolverSessionFactory;
+import org.mulgara.store.xa.XAStatementStore;
+
+/**
+ * An extension of StatementStoreResolverFactory which instantiates an XA 1.1 statement store.
+ *
+ * @created Oct 1, 2008
+ * @author Paul Gearon
+ * @copyright &copy; 2008 <a href="http://www.topazproject.org/">The Topaz Project</a>
+ * @licence <a href="{@docRoot}/../../LICENCE.txt">Open Software License v3.0</a>
+ */
+public class XA11StatementStoreResolverFactory extends StatementStoreResolverFactory {
+
+  /**
+   * Internal construction of a resolver factory.
+   * @throws InitializerException Unable to initialize.
+   */
+  protected XA11StatementStoreResolverFactory(FactoryInitializer initializer,
+      XAResolverSessionFactory resolverSessionFactory) throws InitializerException {
+    super(initializer, resolverSessionFactory);
+  }
+
+  /**
+   * Factory method for instances of this factory
+   * @param initializer Configuration object for this factory.
+   * @param resolverSessionFactory A factory that this factory can proxy for getting sessions. 
+   * @return A new factory for resolver sessions.
+   * @throws InitializerException The factory could not be initialized.
+   */
+  public static ResolverFactory newInstance(FactoryInitializer initializer,
+        XAResolverSessionFactory resolverSessionFactory) throws InitializerException {
+    return new XA11StatementStoreResolverFactory(initializer, resolverSessionFactory);
+  }
+
+  /**
+   * Creates the required type of store
+   * @param filePrefix The base for the files being used for storage.
+   * @return a new instance of an XAStatementStore
+   * @throws IOException Error accessing the filesystem
+   */
+  protected XAStatementStore createStore(String filePrefix) throws IOException {
+    return new XA11StatementStoreImpl(filePrefix.toString());
+  }
+}

Modified: branches/xa11/src/jar/resolver-store/java/org/mulgara/store/statement/xa/TripleAVLFile.java
===================================================================
--- branches/xa11/src/jar/resolver-store/java/org/mulgara/store/statement/xa/TripleAVLFile.java	2008-12-02 18:59:07 UTC (rev 1388)
+++ branches/xa11/src/jar/resolver-store/java/org/mulgara/store/statement/xa/TripleAVLFile.java	2008-12-02 20:08:36 UTC (rev 1389)
@@ -1386,7 +1386,7 @@
     }
 
 
-    long checkIntegrity() {
+    public long checkIntegrity() {
       if (this == currentPhase && tripleWriteThread != null)
         tripleWriteThread.drain();
 

Added: branches/xa11/src/jar/resolver-store/java/org/mulgara/store/statement/xa11/XA11StatementStoreImpl.java
===================================================================
--- branches/xa11/src/jar/resolver-store/java/org/mulgara/store/statement/xa11/XA11StatementStoreImpl.java	                        (rev 0)
+++ branches/xa11/src/jar/resolver-store/java/org/mulgara/store/statement/xa11/XA11StatementStoreImpl.java	2008-12-02 20:08:36 UTC (rev 1389)
@@ -0,0 +1,1594 @@
+/*
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * The Original Code is the Kowari Metadata Store.
+ *
+ * The Initial Developer of the Original Code is Plugged In Software Pty
+ * Ltd (http://www.pisoftware.com, mailto:info at pisoftware.com). Portions
+ * created by Plugged In Software Pty Ltd are Copyright (C) 2001,2002
+ * Plugged In Software Pty Ltd. All Rights Reserved.
+ *
+ * Contributor(s): N/A.
+ *
+ * [NOTE: The text of this Exhibit A may differ slightly from the text
+ * of the notices in the Source Code files of the Original Code. You
+ * should use the text of this Exhibit A rather than the text found in the
+ * Original Code Source Code for Your Modifications.]
+ *
+ */
+package org.mulgara.store.statement.xa11;
+
+import java.io.*;
+import java.nio.*;
+
+// Java 2 standard packages
+import java.util.*;
+
+// Third party packages
+import org.apache.log4j.Logger;
+
+// Locally written packages
+import org.mulgara.query.*;
+import org.mulgara.store.nodepool.*;
+import org.mulgara.store.statement.*;
+import org.mulgara.store.statement.xa.TripleAVLFile;
+import org.mulgara.store.tuples.StoreTuples;
+import org.mulgara.store.tuples.TuplesOperations;
+import org.mulgara.store.xa.AbstractBlockFile;
+import org.mulgara.store.xa.Block;
+import org.mulgara.store.xa.BlockFile;
+import org.mulgara.store.xa.LockFile;
+import org.mulgara.store.xa.PersistableMetaRoot;
+import org.mulgara.store.xa.SimpleXAResourceException;
+import org.mulgara.store.xa.XAStatementStore;
+import org.mulgara.store.xa.XAUtils;
+import org.mulgara.util.Constants;
+
+/**
+ * An implementation of {@link StatementStore}.
+ *
+ * @created 2008-09-30
+ * @author Paul Gearon
+ * @company <A href="mailto:info at PIsoftware.com">Plugged In Software</A>
+ * @copyright &copy;2001-2004 <a href="http://www.pisoftware.com/">Plugged In Software Pty Ltd</a>
+ * @licence <a href="{@docRoot}/../../LICENCE">Mozilla Public License v1.1</a>
+ */
+public final class XA11StatementStoreImpl implements XAStatementStore {
+
+  /** Logger. */
+  private final static Logger logger = Logger.getLogger(XA11StatementStoreImpl.class);
+
+  /** The value of the invalid gNode */
+  final static long NONE = NodePool.NONE;
+
+  /** The subject/predicate/object index */
+  final static int TI_3012 = 0;
+
+  /** The predicate/object/subject index */
+  final static int TI_3120 = 1;
+
+  /** The object/subject/predicate index */
+  final static int TI_3201 = 2;
+
+  /** The number of indexes */
+  final static int NR_INDEXES = 3;
+
+  /** The ordering of indexes, as indexed by the TI_ values */
+  private final static int[][] orders = {
+      {3, 0, 1, 2},  // TI_3012
+      {3, 1, 2, 0},  // TI_3120
+      {3, 2, 0, 1}   // TI_3201
+  };
+
+  private final static int[] selectIndex = {
+    /* 3XXX */ TI_3012,
+    /* 3XX0 */ TI_3012,
+    /* 3X1X */ TI_3120,
+    /* 3X10 */ TI_3012,
+    /* 32XX */ TI_3201,
+    /* 32X0 */ TI_3201,
+    /* 321X */ TI_3120,
+    /* 3210 */ TI_3012
+  };
+
+  /** A number to identify the correct file type */
+  private final static int FILE_MAGIC = 0xa5e7f21e;
+
+  /** The version of file format */
+  private final static int FILE_VERSION = 9;
+
+  /** Index of the file magic number within each of the two on-disk metaroots. */
+  private final static int IDX_MAGIC = 0;
+
+  /** Index of the file version number within each of the two on-disk metaroots. */
+  private final static int IDX_VERSION = 1;
+
+  /** Index of the valid flag (in ints) within each of the two on-disk metaroots. */
+  private final static int IDX_VALID = 2;
+
+  /** The index of the phase number in the on-disk phase. */
+  private final static int IDX_PHASE_NUMBER = 3;
+
+  /** The size of the header of a metaroot in ints. */
+  private final static int HEADER_SIZE_INTS = 4;
+
+  /** The size of the header of a metaroot in longs. */
+  private final static int HEADER_SIZE_LONGS = (HEADER_SIZE_INTS + 1) / 2;
+
+  /** The size of a metaroot in longs. */
+  private final static int METAROOT_SIZE = HEADER_SIZE_LONGS + Phase.RECORD_SIZE;
+
+  /** The number of metaroots in the metaroot file. */
+  private final static int NR_METAROOTS = 2;
+
+  /** The mask for a bound Subject */
+  private final static int MASK0 = 1;
+
+  /** The mask for a bound Predicate */
+  private final static int MASK1 = 2;
+
+  /** The mask for a bound Object */
+  private final static int MASK2 = 4;
+
+  /** The mask for a bound Graph. This must always be set. */
+  private final static int MASK3 = 8;
+
+  /** The name of the triple store which forms the base name for the graph files. */
+  private String fileName;
+
+  /** The LockFile that protects the graph from being opened twice. */
+  private LockFile lockFile;
+
+  /** The BlockFile for the node pool metaroot file. */
+  private BlockFile metarootFile = null;
+
+  /** The metaroot blocks of the metaroot file. */
+  private Block[] metarootBlocks = new Block[NR_METAROOTS];
+
+  /** An error flag that is set during file initialization if the file version is incorrect */
+  private boolean wrongFileVersion = false;
+
+  /** The files containing indexed triples */
+  private TripleAVLFile[] tripleAVLFiles = new TripleAVLFile[NR_INDEXES];
+
+  /** The current read/write phase. Only the latest phase can write. */
+  private Phase currentPhase = null;
+
+  /**
+   * Determines if modifications can be performed without creating a new
+   * (in-memory) phase. If dirty is false and the current phase is in use (by
+   * unclosed Tupleses) then a new phase must be created to protect the existing
+   * Tupleses before any further modifications are made.
+   */
+  private boolean dirty = true;
+
+  /**
+   * The index of the phase in the metaroot. May be 0 or 1 as the commited phase swaps
+   * between the two metaroots.
+   */
+  private int phaseIndex = 0;
+
+  /** The number of the current phase */
+  private int phaseNumber = 0;
+
+  /** A reference token for keeping the commited phase available until we no longer need it */
+  private Phase.Token committedPhaseToken = null;
+
+  /** A synchronization object for locking access to the committed phase */
+  private Object committedPhaseLock = new Object();
+
+  /** A reference token for keeping the recording phase available until we no longer need it */
+  private Phase.Token recordingPhaseToken = null;
+
+  /**
+   * This flag indicates that the current object has been fully written, and may be considered
+   * as committed when the rest of the system is ready.
+   */
+  private boolean prepared = false;
+
+  /** A set of objects to be informed when nodes are released. */
+  private List<ReleaseNodeListener> releaseNodeListeners = new ArrayList<ReleaseNodeListener>();
+
+
+  /**
+   * Creates a statement store using a base filename.
+   *
+   * @param fileName The base filename to operate from.
+   * @throws IOException The mass storage could not be accessed.
+   */
+  public XA11StatementStoreImpl(String fileName) throws IOException {
+    this.fileName = fileName;
+
+    lockFile = LockFile.createLockFile(fileName + ".g.lock");
+
+    try {
+      // Check that the metaroot file was created with a compatible version of the triplestore.
+      RandomAccessFile metarootRAF = null;
+      try {
+        metarootRAF = new RandomAccessFile(fileName + ".g", "r");
+        if (metarootRAF.length() >= 2 * Constants.SIZEOF_INT) {
+          int fileMagic = metarootRAF.readInt();
+          int fileVersion = metarootRAF.readInt();
+          if (AbstractBlockFile.byteOrder != ByteOrder.BIG_ENDIAN) {
+            fileMagic = XAUtils.bswap(fileMagic);
+            fileVersion = XAUtils.bswap(fileVersion);
+          }
+          wrongFileVersion = fileMagic != FILE_MAGIC || fileVersion != FILE_VERSION;
+        } else {
+          wrongFileVersion = false;
+        }
+      } catch (FileNotFoundException ex) {
+        wrongFileVersion = false;
+      } finally {
+        if (metarootRAF != null) metarootRAF.close();
+      }
+
+      for (int i = 0; i < NR_INDEXES; ++i) {
+        String suffix = ".g_" + orders[i][0] + orders[i][1] + orders[i][2] + orders[i][3];
+        tripleAVLFiles[i] = new TripleAVLFile(fileName + suffix, orders[i]);
+      }
+    } catch (IOException ex) {
+      try {
+        close();
+      } catch (StatementStoreException ex2) { /* no-op */ }
+      throw ex;
+    }
+  }
+
+
+  /**
+   * Returns <code>true</code> if there are no triples in the graph
+   * @return <code>true</code> if there are no triples in the graph
+   */
+  public synchronized boolean isEmpty() {
+    checkInitialized();
+    return currentPhase.isEmpty();
+  }
+
+
+  /**
+   * Returns a count of the number of triples in the graph
+   * @return a count of the number of triples in the graph
+   */
+  public synchronized long getNrTriples() {
+    checkInitialized();
+    return currentPhase.getNrTriples();
+  }
+
+
+  /**
+   * Gets the PhaseNumber attribute of the XAGraphImpl object
+   * @return The PhaseNumber value
+   */
+  public synchronized int getPhaseNumber() {
+    checkInitialized();
+    return phaseNumber;
+  }
+
+
+  /**
+   * Adds a feature to the ReleaseNodeListener attribute of the XAGraphImpl object
+   * @param l The feature to be added to the ReleaseNodeListener attribute
+   */
+  public synchronized void addReleaseNodeListener(ReleaseNodeListener l) {
+    if (!releaseNodeListeners.contains(l)) releaseNodeListeners.add(l);
+  }
+
+
+  /**
+   * Removes a release node listener.
+   * @param l The listener to remove.
+   */
+  public synchronized void removeReleaseNodeListener(ReleaseNodeListener l) {
+    releaseNodeListeners.remove(l);
+  }
+
+
+  /**
+   * Adds a new triple to the graph if it doesn't already exist.
+   * @param node0 the first element of the new triple
+   * @param node1 the second element of the new triple
+   * @param node2 the third element of the new triple
+   * @param node3 the fourth element of the new triple
+   * @throws StatementStoreException Due to structural or IO errors.
+   */
+  public synchronized void addTriple(long node0, long node1, long node2, long node3) throws StatementStoreException {
+    checkInitialized();
+    if (
+        node0 < NodePool.MIN_NODE ||
+        node1 < NodePool.MIN_NODE ||
+        node2 < NodePool.MIN_NODE ||
+        node3 < NodePool.MIN_NODE
+    ) {
+      throw new StatementStoreException(
+          "Attempt to add a triple with node number out of range: " + node0 + " " + node1 + " " + node2 + " " + node3
+      );
+    }
+
+    if (!dirty && currentPhase.isInUse()) {
+      try {
+        new Phase();
+      } catch (IOException ex) {
+        throw new StatementStoreException("I/O error", ex);
+      }
+    }
+
+    currentPhase.addTriple(node0, node1, node2, node3);
+  }
+
+
+  /**
+   * Removes all triples matching the given specification.
+   * @param node0 the value for the first element of the triples
+   * @param node1 the value for the second element of the triples
+   * @param node2 the value for the third element of the triples
+   * @param node3 the value for the fourth element of the triples
+   * @throws StatementStoreException if something exceptional happens
+   */
+  public synchronized void removeTriples(long node0, long node1, long node2, long node3) throws StatementStoreException {
+    checkInitialized();
+    if (node0 != NONE && node1 != NONE && node2 != NONE && node3 != NONE) {
+      if (!dirty && currentPhase.isInUse()) {
+        try {
+          new Phase();
+        } catch (IOException ex) {
+          throw new StatementStoreException("I/O error", ex);
+        }
+      }
+
+      // Remove the triple.
+      currentPhase.removeTriple(node0, node1, node2, node3);
+    } else {
+      // Find all the tuples matching the specification and remove them.
+      StoreTuples tuples = currentPhase.findTuples(node0, node1, node2, node3);
+      try {
+        try {
+          if (!tuples.isEmpty()) {
+            // There is at least one triple to remove so protect the
+            // Tuples as we make changes to the triplestore.
+            try {
+              new Phase();
+            } catch (IOException ex) {
+              throw new StatementStoreException("I/O error", ex);
+            }
+
+            long[] triple = new long[] { node0, node1, node2, node3 };
+            int[] columnMap = tuples.getColumnOrder();
+            int nrColumns = columnMap.length;
+            tuples.beforeFirst();
+            while (tuples.next()) {
+              // Copy the row data over to the triple.
+              for (int col = 0; col < nrColumns; ++col) {
+                triple[columnMap[col]] = tuples.getColumnValue(col);
+              }
+
+              currentPhase.removeTriple(triple[0], triple[1], triple[2], triple[3]);
+            }
+          }
+        } finally {
+          tuples.close();
+        }
+      } catch (TuplesException ex) {
+        throw new StatementStoreException("Exception while iterating over temporary Tuples.", ex);
+      }
+    }
+  }
+
+
+  /**
+   * Finds triples matching the given specification.
+   * @param node0 The 0 node of the triple to find.
+   * @param node1 The 1 node of the triple to find.
+   * @param node2 The 2 node of the triple to find.
+   * @param node3 The 3 node of the triple to find.
+   * @return A set of all the triples which match the search.
+   * @throws StatementStoreException Due to a structural or IO error.
+   */
+  public synchronized StoreTuples findTuples(long node0, long node1, long node2, long node3) throws StatementStoreException {
+    checkInitialized();
+    dirty = false;
+    return currentPhase.findTuples(node0, node1, node2, node3);
+  }
+
+  
+  /**
+   * Finds triples matching the given specification and index mask.
+   * @param mask The mask of the index to use. This is only allowable for 3 variables
+   *             and a given graph.
+   * @param node0 The 0 node of the triple to find.
+   * @param node1 The 1 node of the triple to find.
+   * @param node2 The 2 node of the triple to find.
+   * @param node3 The 3 node of the triple to find.
+   * @return A set of all the triples which match the search.
+   * @throws StatementStoreException Due to a structural or IO error.
+   */
+  public synchronized StoreTuples findTuples(
+      int mask, long node0, long node1, long node2, long node3
+  ) throws StatementStoreException {
+    checkInitialized();
+    dirty = false;
+    if (!checkMask(mask, node0, node1, node2, node3)) throw new StatementStoreException("Bad explicit index selection for given node pattern.");
+    return currentPhase.findTuples(mask, node0, node1, node2, node3);
+  }
+
+
+  /**
+   * Tests a mask for consistency against the nodes it will be used to find.
+   * @param mask The mask to test.
+   * @param node0 The 0 node of the triple to find.
+   * @param node1 The 1 node of the triple to find.
+   * @param node2 The 2 node of the triple to find.
+   * @param node3 The 3 node of the triple to find. Must not be NONE.
+   * @return <code>true</code> if the mask is consistent with the given nodes.
+   */
+  private static boolean checkMask(int mask, long node0, long node1, long node2, long node3) {
+    // The graph must be bound
+    if (node3 != NONE) return false;
+    if (node0 != NONE && 0 == (mask & MASK0)) return false;
+    if (node1 != NONE && 0 == (mask & MASK1)) return false;
+    if (node2 != NONE && 0 == (mask & MASK2)) return false;
+    return true;
+  }
+
+
+  /**
+   * Returns a StoreTuples which contains all triples in the store.  The
+   * parameters provide a hint about how the StoreTuples will be used.  This
+   * information is used to select the index from which the StoreTuples will be
+   * obtained.
+   * @param node0Bound specifies that node0 will be bound
+   * @param node1Bound specifies that node1 will be bound
+   * @param node2Bound specifies that node2 will be bound
+   * @return the {@link StoreTuples}
+   * @throws StatementStoreException if something exceptional happens
+   */
+  public synchronized StoreTuples findTuples(boolean node0Bound, boolean node1Bound, boolean node2Bound, boolean node3Bound) throws StatementStoreException {
+    if (!node3Bound) throw new IllegalArgumentException("The graph must be bound");
+    checkInitialized();
+    dirty = false;
+    return currentPhase.findTuples(node0Bound, node1Bound, node2Bound);
+  }
+
+
+  /**
+   * Returns <code>true</code> if any triples match the given specification.
+   * Allows wild cards StatementStore.NONE for any of the node numbers except node3.
+   * @param node0 The 0 node of the triple to find.
+   * @param node1 The 1 node of the triple to find.
+   * @param node2 The 2 node of the triple to find.
+   * @param node3 The 3 node of the triple to find.
+   * @return <code>true</code> if any matching triples exist in the graph.
+   * @throws StatementStoreException Due to a structural or IO error.
+   */
+  public synchronized boolean existsTriples(long node0, long node1, long node2, long node3) throws StatementStoreException {
+    checkInitialized();
+    return currentPhase.existsTriples(node0, node1, node2, node3);
+  }
+
+
+  public XAStatementStore newReadOnlyStatementStore() {
+    return new ReadOnlyGraph();
+  }
+
+
+  public XAStatementStore newWritableStatementStore() {
+    return this;
+  }
+
+
+  /**
+   * Close all files, removing empty space from the ends as required.
+   * @throws StatementStoreException if an error occurs while truncating,
+   * flushing or closing one of the three files.
+   */
+  public synchronized void close() throws StatementStoreException {
+    try {
+      unmap();
+    } finally {
+      try {
+        IOException savedEx = null;
+
+        for (int i = 0; i < NR_INDEXES; ++i) {
+          try {
+            if (tripleAVLFiles[i] != null) tripleAVLFiles[i].close();
+          } catch (IOException ex) {
+            savedEx = ex;
+          }
+        }
+
+        if (metarootFile != null) {
+          try {
+            metarootFile.close();
+          } catch (IOException ex) {
+            savedEx = ex;
+          }
+        }
+
+        if (savedEx != null) throw new StatementStoreException("I/O error closing graph.", savedEx);
+      } finally {
+        if (lockFile != null) {
+          lockFile.release();
+          lockFile = null;
+        }
+      }
+    }
+  }
+
+
+  /**
+   * Close this graph, if it is currently open, and remove all files associated with it.
+   * @throws StatementStoreException Due to an IO error.
+   */
+  public synchronized void delete() throws StatementStoreException {
+    currentPhase = null;
+    try {
+      unmap();
+    } finally {
+      try {
+        IOException savedEx = null;
+
+        for (int i = 0; i < NR_INDEXES; ++i) {
+          try {
+            if (tripleAVLFiles[i] != null) tripleAVLFiles[i].delete();
+          } catch (IOException ex) {
+            savedEx = ex;
+          }
+        }
+
+        if (metarootFile != null) {
+          try {
+            metarootFile.delete();
+          } catch (IOException ex) {
+            savedEx = ex;
+          }
+        }
+
+        if (savedEx != null) throw new StatementStoreException("I/O error deleting graph.", savedEx);
+      } finally {
+        for (int i = 0; i < NR_INDEXES; ++i) tripleAVLFiles[i] = null;
+        metarootFile = null;
+        if (lockFile != null) {
+          lockFile.release();
+          lockFile = null;
+        }
+      }
+    }
+  }
+
+
+  /**
+   * Try to safely close the store if this was not done explicitly.
+   */
+  protected void finalize() {
+    try {
+      close();
+    } catch (Throwable t) {
+      logger.warn("Exception in finalize while trying to close the statement store.", t);
+    }
+  }
+
+
+  /**
+   * A manually tracked reference to this object was released. Does nothing.
+   */
+  public void release() {
+    if (logger.isDebugEnabled()) logger.debug("Release " + this.getClass() + ":" + System.identityHashCode(this));
+  }
+
+
+  /**
+   * This in called in response to the resource being manually refreshed.
+   * This implementation does nothing here.
+   */
+  public void refresh() {
+    if (logger.isDebugEnabled()) {
+      logger.debug("Refresh " + this.getClass() + ":" + System.identityHashCode(this));
+    }
+  }
+
+
+  //
+  // Methods from SimpleXAResource.
+  //
+
+  /**
+   * Clears this store to a fresh state.
+   * @param phaseNumber The phase number to set to.
+   * @throws IOException Error with file access
+   * @throws SimpleXAResourceException Error with the data structures.
+   */
+  public synchronized void clear(int phaseNumber) throws IOException, SimpleXAResourceException {
+    if (logger.isDebugEnabled()) {
+      logger.debug("Clear(" + phaseNumber + ") " + this.getClass() + ":" + System.identityHashCode(this));
+    }
+    if (currentPhase != null) throw new IllegalStateException("Graph already has a current phase.");
+
+    openMetarootFile(true);
+
+    synchronized (committedPhaseLock) {
+      committedPhaseToken = new Phase().use();
+    }
+    this.phaseNumber = phaseNumber;
+    phaseIndex = 1;
+    for (int i = 0; i < NR_INDEXES; ++i) tripleAVLFiles[i].clear();
+
+    new Phase();
+  }
+
+
+  /**
+   * Clear the state of the database.
+   * @throws IOException Filesystem error
+   * @throws SimpleXAResourceException Error in the data structures.
+   */
+  public synchronized void clear() throws IOException, SimpleXAResourceException {
+    if (logger.isDebugEnabled()) logger.debug("Clear " + this.getClass() + ":" + System.identityHashCode(this));
+    if (currentPhase == null) clear(0);
+
+    // could throw an exception if clear() is called after any other
+    // operations are performed.  Calling clear() multiple times should be
+    // permitted.
+  }
+
+
+  /**
+   * Perform all the operations for a commit and return when all the data structures are in place.
+   * @throws SimpleXAResourceException Due to a bad transaction state, or an IO error while preparing.
+   */
+  public synchronized void prepare() throws SimpleXAResourceException {
+    if (logger.isDebugEnabled()) logger.debug("Prepare " + this.getClass() + ":" + System.identityHashCode(this));
+    checkInitialized();
+
+    // check that prepare() was not caleld twice
+    if (prepared) throw new SimpleXAResourceException("prepare() called twice.");
+
+    try {
+      // Perform a prepare.
+      recordingPhaseToken = currentPhase.use();
+      Phase recordingPhase = currentPhase;
+      new Phase();
+
+      // Ensure that all data associated with the phase is on disk.
+      for (int i = 0; i < NR_INDEXES; ++i) tripleAVLFiles[i].force();
+
+      // Write the metaroot.
+      int newPhaseIndex = 1 - phaseIndex;
+      int newPhaseNumber = phaseNumber + 1;
+
+      Block block = metarootBlocks[newPhaseIndex];
+      block.putInt(IDX_VALID, 0); // should already be invalid.
+      block.putInt(IDX_PHASE_NUMBER, newPhaseNumber);
+      logger.debug("Writing graph metaroot for phase: " + newPhaseNumber);
+      recordingPhase.writeToBlock(block, HEADER_SIZE_LONGS);
+      block.write();
+      metarootFile.force();
+      block.putInt(IDX_VALID, 1);
+      block.write();
+      metarootFile.force();
+
+      phaseIndex = newPhaseIndex;
+      phaseNumber = newPhaseNumber;
+      prepared = true;
+    } catch (IOException ex) {
+      logger.error("I/O error while performing prepare.", ex);
+      throw new SimpleXAResourceException("I/O error while performing prepare.", ex);
+    } finally {
+      if (!prepared) {
+        // Something went wrong. An exception is on its way out
+        logger.error("Prepare failed.");
+        if (recordingPhaseToken != null) {
+          recordingPhaseToken.release();
+          recordingPhaseToken = null;
+        }
+      }
+    }
+  }
+
+
+  /**
+   * Update the metadata to point to the prepared data structures.
+   * @throws SimpleXAResourceException Due to a bad transaction state, or an IO error.
+   */
+  public synchronized void commit() throws SimpleXAResourceException {
+    if (logger.isDebugEnabled()) logger.debug("Commit " + this.getClass() + ":" + System.identityHashCode(this));
+
+    // check that prepare has been called
+    if (!prepared) throw new SimpleXAResourceException("commit() called without previous prepare().");
+
+    // Perform a commit.
+    try {
+      // Invalidate the metaroot of the old phase.
+      Block block = metarootBlocks[1 - phaseIndex];
+      block.putInt(IDX_VALID, 0);
+      block.write();
+      metarootFile.force();
+
+      // Release the token for the previously committed phase.
+      synchronized (committedPhaseLock) {
+        if (committedPhaseToken != null) committedPhaseToken.release();
+        committedPhaseToken = recordingPhaseToken;
+      }
+      recordingPhaseToken = null;
+    } catch (IOException ex) {
+      logger.fatal("I/O error while performing commit.", ex);
+      throw new SimpleXAResourceException("I/O error while performing commit.", ex);
+    } finally {
+      prepared = false;
+      if (recordingPhaseToken != null) {
+        // Something went wrong! An exception is on its way out
+        recordingPhaseToken.release();
+        recordingPhaseToken = null;
+
+        logger.error("Commit failed.  Calling close().");
+        try {
+          close();
+        } catch (Throwable t) {
+          logger.error("Exception on forced close()", t);
+        }
+      }
+    }
+  }
+
+
+  /**
+   * Read the state from the metaroot file and use it to set up this object
+   * @return An array of 0, 1, or 2 valid phases that can be selected as the last committed phase.
+   * @throws SimpleXAResourceException Due to an IO error, or a data error in the metaroot file.
+   */
+  public synchronized int[] recover() throws SimpleXAResourceException {
+    if (logger.isDebugEnabled()) logger.debug("Recover " + this.getClass() + ":" + System.identityHashCode(this));
+    if (currentPhase != null) return new int[0];
+    if (wrongFileVersion) throw new SimpleXAResourceException("Wrong metaroot file version.");
+
+    try {
+      openMetarootFile(false);
+    } catch (IOException ex) {
+      throw new SimpleXAResourceException("I/O error", ex);
+    }
+
+    // Count the number of valid phases.
+    int phaseCount = 0;
+    if (metarootBlocks[0].getInt(IDX_VALID) != 0) ++phaseCount;
+    if (metarootBlocks[1].getInt(IDX_VALID) != 0) ++phaseCount;
+
+    // Read the phase numbers.
+    int[] phaseNumbers = new int[phaseCount];
+    int index = 0;
+    if (metarootBlocks[0].getInt(IDX_VALID) != 0) phaseNumbers[index++] = metarootBlocks[0].getInt(IDX_PHASE_NUMBER);
+    if (metarootBlocks[1].getInt(IDX_VALID) != 0) phaseNumbers[index++] = metarootBlocks[1].getInt(IDX_PHASE_NUMBER);
+    return phaseNumbers;
+  }
+
+
+  /**
+   * Choose a phase from the metaroot file to use
+   * @param phaseNumber The number of the phase to select. This must be one of the valid
+   *        phases present in the metaroot file.
+   * @throws IOException Due to an error on the filesystem
+   * @throws SimpleXAResourceException If the file structures are incorrect.
+   */
+  public synchronized void selectPhase(int phaseNumber) throws IOException, SimpleXAResourceException {
+    if (logger.isDebugEnabled()) {
+      logger.debug("SelectPhase(" + phaseNumber + ") " + this.getClass() + ":" + System.identityHashCode(this));
+    }
+    if (currentPhase != null) throw new SimpleXAResourceException("selectPhase() called on initialized Graph.");
+    if (metarootFile == null) throw new SimpleXAResourceException("Graph metaroot file is not open.");
+
+    // Locate the metaroot corresponding to the given phase number.
+    if (
+        metarootBlocks[0].getInt(IDX_VALID) != 0 &&
+        metarootBlocks[0].getInt(IDX_PHASE_NUMBER) == phaseNumber
+    ) {
+      phaseIndex = 0;
+      // A new phase will be saved in the other metaroot.
+    } else if (
+        metarootBlocks[1].getInt(IDX_VALID) != 0 &&
+        metarootBlocks[1].getInt(IDX_PHASE_NUMBER) == phaseNumber
+    ) {
+      phaseIndex = 1;
+      // A new phase will be saved in the other metaroot.
+    } else {
+      throw new SimpleXAResourceException("Invalid phase number: " + phaseNumber);
+    }
+
+    // Load a duplicate of the selected phase.  The duplicate will have a
+    // phase number which is one higher than the original phase.
+    try {
+      synchronized (committedPhaseLock) {
+        committedPhaseToken = new Phase(metarootBlocks[phaseIndex], HEADER_SIZE_LONGS).use();
+      }
+      this.phaseNumber = phaseNumber;
+    } catch (IllegalStateException ex) {
+      throw new SimpleXAResourceException("Cannot construct initial phase.", ex);
+    }
+    new Phase();
+
+    // Invalidate the on-disk metaroot that the new phase will be saved to.
+    Block block = metarootBlocks[1 - phaseIndex];
+    block.putInt(IDX_VALID, 0);
+    block.write();
+    metarootFile.force();
+  }
+
+
+  /**
+   * Return to the data structure state from the beginning of the transaction.
+   * @throws SimpleXAResourceException Due to an IO error.
+   */
+  public synchronized void rollback() throws SimpleXAResourceException {
+    if (logger.isDebugEnabled()) logger.debug("Rollback " + this.getClass() + ":" + System.identityHashCode(this));
+    checkInitialized();
+    try {
+      if (prepared) {
+        // Restore phaseIndex and phaseNumber to their previous values.
+        phaseIndex = 1 - phaseIndex;
+        --phaseNumber;
+        recordingPhaseToken = null;
+        prepared = false;
+
+        // Invalidate the metaroot of the other phase.
+        Block block = metarootBlocks[1 - phaseIndex];
+        block.putInt(IDX_VALID, 0);
+        block.write();
+        metarootFile.force();
+      }
+    } catch (IOException ex) {
+      throw new SimpleXAResourceException("I/O error while performing rollback (invalidating metaroot)", ex);
+    } finally {
+      try {
+        new Phase(committedPhaseToken.getPhase());
+      } catch (IOException ex) {
+        throw new SimpleXAResourceException("I/O error while performing rollback (new committed phase)", ex);
+      }
+    }
+  }
+
+
+  /**
+   * Get a string representation of the current state of the graph.
+   * @return A string representing the current state
+   */
+  public synchronized String toString() {
+    if (currentPhase == null) return "Uninitialized Graph.";
+    return currentPhase.toString();
+  }
+
+
+  /**
+   * Attempt to cleanly close all mapped files.
+   */
+  public synchronized void unmap() {
+    if (committedPhaseToken != null) {
+      recordingPhaseToken = null;
+      prepared = false;
+
+      try {
+        new Phase(committedPhaseToken.getPhase());
+      } catch (Throwable t) {
+        logger.warn("Exception while rolling back in unmap()", t);
+      }
+      currentPhase = null;
+
+      synchronized (committedPhaseLock) {
+        committedPhaseToken.release();
+        committedPhaseToken = null;
+      }
+    }
+
+    if (tripleAVLFiles != null) {
+      for (int i = 0; i < NR_INDEXES; ++i) {
+        if (tripleAVLFiles[i] != null) tripleAVLFiles[i].unmap();
+      }
+    }
+
+    if (metarootFile != null) {
+      if (metarootBlocks[0] != null) metarootBlocks[0] = null;
+      if (metarootBlocks[1] != null) metarootBlocks[1] = null;
+      metarootFile.unmap();
+    }
+  }
+
+
+  /**
+   * Check that the data structures are valid
+   * @return The number of triples in the database
+   */
+  synchronized long checkIntegrity() {
+    checkInitialized();
+    return currentPhase.checkIntegrity();
+  }
+
+
+  /**
+   * Open the metaroot file and read in the contents
+   * @param clear If <code>true</code> then the file will be reset to empty.
+   * @throws IOException Due to a filesystem error.
+   * @throws SimpleXAResourceException If the data structures are inconsistent.
+   */
+  private void openMetarootFile(boolean clear) throws IOException, SimpleXAResourceException {
+    if (metarootFile == null) {
+      metarootFile = AbstractBlockFile.openBlockFile(fileName + ".g", METAROOT_SIZE * Constants.SIZEOF_LONG, BlockFile.IOType.EXPLICIT);
+
+      long nrBlocks = metarootFile.getNrBlocks();
+      if (nrBlocks != NR_METAROOTS) {
+        if (nrBlocks > 0) {
+          logger.info("Graph metaroot file for triple store \"" + fileName + "\" has invalid number of blocks: " + nrBlocks);
+          if (nrBlocks < NR_METAROOTS) {
+            clear = true;
+            metarootFile.clear();
+          }
+        } else {
+          // Perform initialization on empty file.
+          clear = true;
+        }
+        metarootFile.setNrBlocks(NR_METAROOTS);
+      }
+
+      metarootBlocks[0] = metarootFile.readBlock(0);
+      metarootBlocks[1] = metarootFile.readBlock(1);
+    }
+
+    if (clear) {
+      // Invalidate the metaroots on disk.
+      metarootBlocks[0].putInt(IDX_MAGIC, FILE_MAGIC);
+      metarootBlocks[0].putInt(IDX_VERSION, FILE_VERSION);
+      metarootBlocks[0].putInt(IDX_VALID, 0);
+      metarootBlocks[0].write();
+      metarootBlocks[1].putInt(IDX_MAGIC, 0);
+      metarootBlocks[1].putInt(IDX_VERSION, 0);
+      metarootBlocks[1].putInt(IDX_VALID, 0);
+      metarootBlocks[1].write();
+      metarootFile.force();
+    }
+  }
+
+
+  /**
+   * Tests that the current object has been initialized.
+   * @throws IllegalStateException Throws this unchecked exception if the object is not initialized.
+   */
+  private void checkInitialized() {
+    if (currentPhase == null) throw new IllegalStateException("No current phase.  Graph has not been initialized or has been closed.");
+  }
+
+
+  final class ReadOnlyGraph implements XAStatementStore {
+
+    private Phase phase = null;
+
+    private Phase.Token token = null;
+
+
+    /**
+     * Create a read-only graph attached to the current outer database
+     */
+    ReadOnlyGraph() {
+      synchronized (committedPhaseLock) {
+        if (committedPhaseToken == null) {
+          throw new IllegalStateException("Cannot create read only view of uninitialized Graph.");
+        }
+      }
+    }
+
+
+    public synchronized boolean isEmpty() {
+      return phase.isEmpty();
+    }
+
+
+    /**
+     * Returns a count of the number of triples in the graph
+     * @return a count of the number of triples in the graph
+     */
+    public synchronized long getNrTriples() {
+      return phase.getNrTriples();
+    }
+
+
+    /**
+     * Adds a triple to the graph.
+     * @param node0 The 0 node of the triple.
+     * @param node1 The 1 node of the triple.
+     * @param node2 The 2 node of the triple.
+     * @param node3 The 3 node of the triple.
+     */
+    public void addTriple(long node0, long node1, long node2, long node3) throws StatementStoreException {
+      throw new UnsupportedOperationException("Trying to modify a read-only graph.");
+    }
+
+
+    /**
+     * Removes all triples matching the given specification.
+     * @param node0 the value for the first element of the triples
+     * @param node1 the value for the second element of the triples
+     * @param node2 the value for the third element of the triples
+     * @param node3 the value for the fourth element of the triples
+     */
+    public void removeTriples(long node0, long node1, long node2, long node3) throws StatementStoreException {
+      throw new UnsupportedOperationException("Trying to modify a read-only graph.");
+    }
+
+
+    /**
+     * Finds triples matching the given specification.
+     * @param node0 The 0 node of the triple to find.
+     * @param node1 The 1 node of the triple to find.
+     * @param node2 The 2 node of the triple to find.
+     * @param node3 The 3 node of the triple to find.
+     * @return A StoreTuples which contains the triples which match the search.
+     */
+    public synchronized StoreTuples findTuples(long node0, long node1, long node2, long node3) throws StatementStoreException {
+      return phase.findTuples(node0, node1, node2, node3);
+    }
+
+    /**
+     * Finds triples matching the given specification.
+     * @param mask The mask of the index to use. This is only allowable for 3 variables
+     *             and a given graph.
+     * @param node0 The 0 node of the triple to find.
+     * @param node1 The 1 node of the triple to find.
+     * @param node2 The 2 node of the triple to find.
+     * @param node3 The 3 node of the triple to find.
+     * @return A StoreTuples which contains the triples which match the search.
+     * @throws StatementStoreException A structural or IO error
+     */
+    public synchronized StoreTuples findTuples(int mask, long node0, long node1, long node2, long node3) throws StatementStoreException {
+      if (!checkMask(mask, node0, node1, node2, node3)) throw new StatementStoreException("Bad explicit index selection for given node pattern.");
+      return phase.findTuples(mask, node0, node1, node2, node3);
+    }
+
+
+    /**
+     * Returns a StoreTuples which contains all triples in the store.  The
+     * parameters provide a hint about how the StoreTuples will be used.  This
+     * information is used to select the index from which the StoreTuples will
+     * be obtained.
+     * @param node0Bound specifies that node0 will be bound
+     * @param node1Bound specifies that node1 will be bound
+     * @param node2Bound specifies that node2 will be bound
+     * @return the {@link StoreTuples}
+     * @throws StatementStoreException if something exceptional happens
+     */
+    public synchronized StoreTuples findTuples(boolean node0Bound, boolean node1Bound, boolean node2Bound, boolean node3Bound) throws StatementStoreException {
+      if (!node3Bound) throw new IllegalArgumentException("The graph must be bound");
+      return phase.findTuples(node0Bound, node1Bound, node2Bound);
+    }
+
+
+    public synchronized boolean existsTriples(long node0, long node1, long node2, long node3) throws StatementStoreException {
+      return phase.existsTriples(node0, node1, node2, node3);
+    }
+
+
+    public XAStatementStore newReadOnlyStatementStore() {
+      throw new UnsupportedOperationException();
+    }
+
+
+    public XAStatementStore newWritableStatementStore() {
+      throw new UnsupportedOperationException();
+    }
+
+
+    public void close() {
+      throw new UnsupportedOperationException("Trying to close a read-only graph.");
+    }
+
+
+    public void delete() {
+      throw new UnsupportedOperationException("Trying to delete a read-only graph.");
+    }
+
+
+    /**
+     * Release the phase.
+     */
+    public synchronized void release() {
+      if (logger.isDebugEnabled()) logger.debug("Releasing " + this.getClass() + ":" + System.identityHashCode(this));
+      try {
+        if (token != null) token.release();
+      } finally {
+        phase = null;
+        token = null;
+      }
+    }
+
+
+    public synchronized void refresh() {
+      if (logger.isDebugEnabled()) logger.debug("Refreshing " + this.getClass() + ":" + System.identityHashCode(this));
+
+      synchronized (committedPhaseLock) {
+        Phase committedPhase = committedPhaseToken.getPhase();
+        if (phase != committedPhase) {
+          if (token != null) token.release();
+          phase = committedPhase;
+          token = phase.use();
+        }
+      }
+    }
+
+    public void addReleaseNodeListener(ReleaseNodeListener l) {
+      throw new UnsupportedOperationException();
+    }
+
+    public void removeReleaseNodeListener(ReleaseNodeListener l) {
+      throw new UnsupportedOperationException();
+    }
+
+    public void prepare() {
+      if (logger.isDebugEnabled()) logger.debug("Preparing " + this.getClass() + ":" + System.identityHashCode(this));
+    }
+
+    public void commit() {
+      if (logger.isDebugEnabled()) logger.debug("Commit " + this.getClass() + ":" + System.identityHashCode(this));
+    }
+
+    public void rollback() {
+      if (logger.isDebugEnabled()) logger.debug("Rollback " + this.getClass() + ":" + System.identityHashCode(this));
+    }
+
+    public void clear() {
+      if (logger.isDebugEnabled()) logger.debug("Clearing " + this.getClass() + ":" + System.identityHashCode(this));
+    }
+
+    public void clear(int phaseNumber) {
+      if (logger.isDebugEnabled()) logger.debug("Clearing (" + phaseNumber + ") " + this.getClass() + ":" + System.identityHashCode(this));
+    }
+
+    public int[] recover() {
+      if (logger.isDebugEnabled()) logger.debug("Recovering " + this.getClass() + ":" + System.identityHashCode(this));
+      throw new UnsupportedOperationException("Attempting to recover ReadOnlyGraph");
+    }
+
+    public void selectPhase(int phaseNumber) {
+      if (logger.isDebugEnabled()) logger.debug("Selecting Phase " + this.getClass() + ":" + System.identityHashCode(this));
+      throw new UnsupportedOperationException("Attempting to selectPhase of ReadOnlyGraph");
+    }
+
+    public int getPhaseNumber() {
+      return phaseNumber;
+    }
+  }
+
+
+  /**
+   * This class represents the state of the the database at a particular time. Only the most
+   * recent phase can be written to.
+   */
+  final class Phase implements PersistableMetaRoot {
+
+    /** The size of the data this object stores in the metaroot */
+    final static int RECORD_SIZE = TripleAVLFile.Phase.RECORD_SIZE * NR_INDEXES;
+
+    /** Maintaines parallel structural phases between all of the parallel tree data structures */
+    private TripleAVLFile.Phase[] tripleAVLFilePhases = new TripleAVLFile.Phase[NR_INDEXES];
+
+
+    /**
+     * Creates a new phase based on the current state of the database.
+     * This sets the latest phase on the outer statement store.
+     * @throws IOException Error on the filesystem.
+     */
+    Phase() throws IOException {
+      for (int i = 0; i < NR_INDEXES; ++i) tripleAVLFilePhases[i] = tripleAVLFiles[i].new Phase();
+      currentPhase = this;
+      dirty = true;
+    }
+
+
+    /**
+     * A copy constructor for duplicating a phase structure. This sets the latest phase
+     * on the outer statement store.
+     * @throws IOException Error on the filesystem.
+     */
+    Phase(Phase p) throws IOException {
+      assert p != null;
+
+      for (int i = 0; i < NR_INDEXES; ++i) tripleAVLFilePhases[i] = tripleAVLFiles[i].new Phase(p.tripleAVLFilePhases[i]);
+      currentPhase = this;
+      dirty = true;
+    }
+
+
+    /**
+     * Create a phase based on information found in a buffer that came from a metaroot
+     * @param b The buffer containing the phase information.
+     * @param offset The start of the phase information in the buffer
+     * @throws IOException A filesystem error occurred while accessing the buffer.
+     */
+    Phase(Block b, int offset) throws IOException {
+      for (int i = 0; i < NR_INDEXES; ++i) {
+        tripleAVLFilePhases[i] = tripleAVLFiles[i].new Phase(b, offset);
+        offset += TripleAVLFile.Phase.RECORD_SIZE;
+      }
+      currentPhase = this;
+      dirty = false;
+    }
+
+
+    /**
+     * Writes this PersistableMetaRoot to the specified Block. The ints are
+     * written at the specified offset.
+     * @param b The metaroot Block to write this object to.
+     * @param offset The start within the buffer of where the phase information should be written to.
+     */
+    public void writeToBlock(Block b, int offset) {
+      for (int i = 0; i < NR_INDEXES; ++i) {
+        tripleAVLFilePhases[i].writeToBlock(b, offset);
+        offset += TripleAVLFile.Phase.RECORD_SIZE;
+      }
+    }
+
+
+    /**
+     * Create a string representation of the current phase.
+     * @return A string representing this phase
+     */
+    public String toString() {
+      StringBuffer sb = new StringBuffer();
+      for (int i = 0; i < NR_INDEXES; ++i) {
+        StoreTuples ts = tripleAVLFilePhases[i].allTuples();
+        try {
+          sb.append(ts).append('\n');
+        } finally {
+          try {
+            ts.close();
+          } catch (TuplesException ex) {
+            logger.warn("TuplesException while closing Tuples", ex);
+            return ex.toString();
+          }
+        }
+      }
+      return sb.toString();
+    }
+
+
+    /**
+     * Tests if any part of this phase has a reference being kept to it
+     * @return <code>true</code> if any part of this phase is being used.
+     */
+    boolean isInUse() {
+      for (int i = 0; i < NR_INDEXES; ++i) {
+        if (tripleAVLFilePhases[i].isInUse()) return true;
+      }
+      return false;
+    }
+
+
+    /**
+     * Tests if the phase contains any triples
+     * @return <code>true</code> if the phase contains 1 or more triples
+     */
+    boolean isEmpty() {
+      return tripleAVLFilePhases[TI_3012].isEmpty();
+    }
+
+
+    /**
+     * Gets the number of triples in the phase
+     * @return The number of triples in this phase
+     */
+    long getNrTriples() {
+      return tripleAVLFilePhases[TI_3012].getNrTriples();
+    }
+
+
+    /**
+     * Adds a new triple to the graph if it doesn't already exist.
+     * @param node0 the first element of the new triple
+     * @param node1 the second element of the new triple
+     * @param node2 the third element of the new triple
+     * @param node3 the fourth element of the new triple
+     * @throws StatementStoreException An IO or data structure error
+     */
+    void addTriple(long node0, long node1, long node2, long node3) throws StatementStoreException {
+      assert node0 >= NodePool.MIN_NODE;
+      assert node1 >= NodePool.MIN_NODE;
+      assert node2 >= NodePool.MIN_NODE;
+      assert node3 >= NodePool.MIN_NODE;
+
+      //if (
+      //  DEBUG && nodePool != null &&
+      //  !nodePool.isValid(node0) && !nodePool.isValid(node1) &&
+      //  !nodePool.isValid(node2) && !nodePool.isValid(node3)
+      //) throw new AssertionError(
+      //  "Attempt to add a triple with an invalid node"
+      //);
+
+      long[] triple = new long[]{node0, node1, node2, node3};
+
+      for (int i = 0; i < NR_INDEXES; ++i) tripleAVLFilePhases[i].asyncAddTriple(triple);
+    }
+
+
+    /**
+     * Removes the specified triple.
+     * @param node0 the value for the first element of the triple
+     * @param node1 the value for the second element of the triple
+     * @param node2 the value for the third element of the triple
+     * @param node3 the value for the fourth element of the triple
+     * @throws StatementStoreException An IO or structural error
+     */
+    void removeTriple(long node0, long node1, long node2, long node3) throws StatementStoreException {
+      if (
+          node0 < NodePool.MIN_NODE ||
+          node1 < NodePool.MIN_NODE ||
+          node2 < NodePool.MIN_NODE ||
+          node3 < NodePool.MIN_NODE
+      ) {
+        throw new StatementStoreException("Attempt to remove a triple with node number out of range: " + node0 + " " + node1 + " " + node2 + " " + node3);
+      }
+
+      try {
+        for (int i = 0; i < NR_INDEXES; ++i) tripleAVLFilePhases[i].removeTriple(node0, node1, node2, node3);
+        // removeTriple listeners can be informed here
+      } catch (IOException e) {
+        throw new StatementStoreException("I/O error", e);
+      }
+    }
+
+
+    /**
+     * Finds triples matching the given specification.
+     * @param variableMask the mask used to indicate the desired index.
+     * @param node0 The 0 node of the triple to find.
+     * @param node1 The 1 node of the triple to find.
+     * @param node2 The 2 node of the triple to find.
+     * @param node3 The 3 node of the triple to find.
+     * @return A StoreTuples containing all the triples which match the search.
+     * @throws StatementStoreException An IO or structural error
+     */
+    StoreTuples findTuples(int variableMask, long node0, long node1, long node2, long node3) throws StatementStoreException {
+      if (
+          node0 < NodePool.NONE ||
+          node1 < NodePool.NONE ||
+          node2 < NodePool.NONE ||
+          node3 < NodePool.NONE
+      ) {
+        // There is at least one query node.  Return an empty StoreTuples.
+        return TuplesOperations.empty();
+      }
+
+      if (0 == (variableMask & MASK3)) throw new StatementStoreException("This version of find is for re-ordering graphs, base on a given mask.");
+      try {
+        switch (variableMask) {
+          case MASK3:
+            return tripleAVLFilePhases[TI_3012].findTuples(node3);
+          case MASK0 | MASK3:
+            return tripleAVLFilePhases[TI_3012].findTuples(node3);
+          case MASK1 | MASK3:
+            return tripleAVLFilePhases[TI_3120].findTuples(node3);
+          case MASK0 | MASK1 | MASK3:
+            return tripleAVLFilePhases[TI_3012].findTuples(node3);
+          case MASK2 | MASK3:
+            return tripleAVLFilePhases[TI_3201].findTuples(node3);
+          case MASK0 | MASK2 | MASK3:
+            return tripleAVLFilePhases[TI_3201].findTuples(node3);
+          case MASK1 | MASK2 | MASK3:
+            return tripleAVLFilePhases[TI_3120].findTuples(node3);
+          case MASK0 | MASK1 | MASK2 | MASK3:
+            return tripleAVLFilePhases[TI_3012].findTuples(node3);
+          default:
+            throw new AssertionError();
+        }
+      } catch (IOException ex) {
+        throw new StatementStoreException("I/O error", ex);
+      }
+    }
+
+
+    /**
+     * Finds triples matching the given specification.
+     * @param node0 The 0 node of the triple to find.
+     * @param node1 The 1 node of the triple to find.
+     * @param node2 The 2 node of the triple to find.
+     * @param node3 The 3 node of the triple to find.
+     * @return A StoreTuples containing all the triples which match the search.
+     * @throws StatementStoreException An IO or structural error
+     */
+    StoreTuples findTuples(long node0, long node1, long node2, long node3) throws StatementStoreException {
+      if (
+          node0 < NodePool.NONE ||
+          node1 < NodePool.NONE ||
+          node2 < NodePool.NONE ||
+          node3 < NodePool.NONE
+      ) {
+        // There is at least one query node.  Return an empty StoreTuples.
+        return TuplesOperations.empty();
+      }
+
+      int variableMask =
+        (node0 != NONE ? MASK0 : 0) |
+        (node1 != NONE ? MASK1 : 0) |
+        (node2 != NONE ? MASK2 : 0) |
+        (node3 != NONE ? MASK3 : 0);
+
+      if (node3 == NONE && variableMask != 0) {
+        throw new IllegalArgumentException("Graph must be specified");
+      }
+
+
+      try {
+        switch (variableMask) {
+          case 0:
+            return tripleAVLFilePhases[TI_3012].allTuples();
+          case MASK3:
+            return tripleAVLFilePhases[TI_3012].findTuples(node3);
+          case MASK0 | MASK3:
+            return tripleAVLFilePhases[TI_3012].findTuples(node3, node0);
+          case MASK1 | MASK3:
+            return tripleAVLFilePhases[TI_3120].findTuples(node3, node1);
+          case MASK0 | MASK1 | MASK3:
+            return tripleAVLFilePhases[TI_3012].findTuples(node3, node0, node1);
+          case MASK2 | MASK3:
+            return tripleAVLFilePhases[TI_3201].findTuples(node3, node2);
+          case MASK0 | MASK2 | MASK3:
+            return tripleAVLFilePhases[TI_3201].findTuples(node3, node2, node0);
+          case MASK1 | MASK2 | MASK3:
+            return tripleAVLFilePhases[TI_3120].findTuples(node3, node1, node2);
+          case MASK0 | MASK1 | MASK2 | MASK3:
+            if (tripleAVLFilePhases[TI_3012].existsTriple(node0, node1, node2, node3)) {
+              return TuplesOperations.unconstrained();
+            }
+            return TuplesOperations.empty();
+          default:
+            throw new AssertionError("Search structure incorrectly calculated");
+        }
+      } catch (IOException ex) {
+        throw new StatementStoreException("I/O error", ex);
+      }
+    }
+
+
+    StoreTuples findTuples(boolean node0Bound, boolean node1Bound, boolean node2Bound) throws StatementStoreException {
+      // The variable mask does not need MASK3, as this has been taken into account in selectIndex[]
+      int variableMask =
+          (node0Bound ? MASK0 : 0) |
+          (node1Bound ? MASK1 : 0) |
+          (node2Bound ? MASK2 : 0);
+
+      return tripleAVLFilePhases[selectIndex[variableMask]].allTuples();
+    }
+
+
+    /**
+     * Test is there exist triples according to a given pattern
+     * @param node0 A subject gNode, or NONE
+     * @param node1 A predicate gNode, or NONE
+     * @param node2 A object gNode, or NONE
+     * @param node3 A subject gNode. May not be NONE
+     * @return <code>true</code> if there exist triples that match the pattern
+     * @throws StatementStoreException A structural or IO exception
+     */
+    boolean existsTriples(long node0, long node1, long node2, long node3) throws StatementStoreException {
+      if (node3 == NONE) throw new IllegalStateException("Graph must be specified");
+
+      if (
+          node0 < NodePool.NONE ||
+          node1 < NodePool.NONE ||
+          node2 < NodePool.NONE ||
+          node3 < NodePool.NONE
+      ) {
+        // There is at least one query node (comes from the query, but not in the data pool).
+        // Return an empty StoreTuples.
+        return false;
+      }
+
+      int variableMask =
+          (node0 != NONE ? MASK0 : 0) |
+          (node1 != NONE ? MASK1 : 0) |
+          (node2 != NONE ? MASK2 : 0) |
+          MASK3;
+
+      try {
+        switch (variableMask) {
+          case MASK3:
+            return tripleAVLFilePhases[TI_3012].existsTriples(node3);
+          case MASK0 | MASK3:
+            return tripleAVLFilePhases[TI_3012].existsTriples(node3, node0);
+          case MASK1 | MASK3:
+            return tripleAVLFilePhases[TI_3120].existsTriples(node3, node1);
+          case MASK0 | MASK1 | MASK3:
+            return tripleAVLFilePhases[TI_3012].existsTriples(node3, node0, node1);
+          case MASK2 | MASK3:
+            return tripleAVLFilePhases[TI_3201].existsTriples(node3, node2);
+          case MASK0 | MASK2 | MASK3:
+            return tripleAVLFilePhases[TI_3201].existsTriples(node3, node2, node0);
+          case MASK1 | MASK2 | MASK3:
+            return tripleAVLFilePhases[TI_3120].existsTriples(node3, node1, node2);
+          case MASK0 | MASK1 | MASK2 | MASK3:
+            return tripleAVLFilePhases[TI_3012].existsTriple(node3, node0, node1, node2);
+          default:
+            throw new AssertionError("Search structure incorrectly calculated");
+        }
+      } catch (IOException ex) {
+        throw new StatementStoreException("I/O error", ex);
+      }
+    }
+
+
+    /**
+     * Check that each index contains the same number of triples
+     * @throws AssertionError if the indexes contain a differing number of triples
+     */ 
+    long checkIntegrity() {
+      long nrTriples[] = new long[NR_INDEXES];
+
+      for (int i = 0; i < NR_INDEXES; ++i) nrTriples[i] = tripleAVLFilePhases[i].checkIntegrity();
+
+      for (int i = 1; i < NR_INDEXES; ++i) {
+        if (nrTriples[0] != nrTriples[i]) {
+          StringBuffer sb = new StringBuffer("tripleAVLFiles disagree on the number of triples:");
+          for (int j = 0; j < NR_INDEXES; ++j) sb.append(' ').append(nrTriples[j]);
+          throw new AssertionError(sb.toString());
+        }
+      }
+
+      return nrTriples[0];
+    }
+
+
+    /**
+     * Increment the reference count on this object.
+     * @return A new token representing the reference.
+     */
+    Token use() {
+      return new Token();
+    }
+
+
+    /**
+     * A token to reference the phase, incrementing the reference count from the perpective
+     * of the garbage collector.
+     */
+    final class Token {
+
+      /** A list of tokens from the underlying indexes */
+      private TripleAVLFile.Phase.Token[] tripleAVLFileTokens = new TripleAVLFile.Phase.Token[NR_INDEXES];
+
+      /** The phase being referenced */
+      private Phase phase = Phase.this;
+
+
+      /**
+       * Creates a token. This creates tokens for the underlying objects as well.
+       */
+      Token() {
+        for (int i = 0; i < NR_INDEXES; ++i) tripleAVLFileTokens[i] = tripleAVLFilePhases[i].use();
+      }
+
+
+      /**
+       * Get the phase that this token represents.
+       * @return The phase referenced by this token.
+       */
+      public Phase getPhase() {
+        assert tripleAVLFileTokens != null : "Invalid Token";
+        return phase;
+      }
+
+
+      /**
+       * Reduce the reference count on the referenced phase by releasing this token.
+       * The token may not be used after being released.
+       */
+      public void release() {
+        assert tripleAVLFileTokens != null : "Invalid Token";
+        for (int i = 0; i < NR_INDEXES; ++i) tripleAVLFileTokens[i].release();
+        tripleAVLFileTokens = null;
+        phase = null;
+      }
+
+    }
+
+  }
+
+}

Added: branches/xa11/src/jar/resolver-store/java/org/mulgara/store/statement/xa11/XA11StatementStoreImplUnitTest.java
===================================================================
--- branches/xa11/src/jar/resolver-store/java/org/mulgara/store/statement/xa11/XA11StatementStoreImplUnitTest.java	                        (rev 0)
+++ branches/xa11/src/jar/resolver-store/java/org/mulgara/store/statement/xa11/XA11StatementStoreImplUnitTest.java	2008-12-02 20:08:36 UTC (rev 1389)
@@ -0,0 +1,149 @@
+/*
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * The Original Code is the Kowari Metadata Store.
+ *
+ * The Initial Developer of the Original Code is Plugged In Software Pty
+ * Ltd (http://www.pisoftware.com, mailto:info at pisoftware.com). Portions
+ * created by Plugged In Software Pty Ltd are Copyright (C) 2001,2002
+ * Plugged In Software Pty Ltd. All Rights Reserved.
+ *
+ * Contributor(s): N/A.
+ *
+ * [NOTE: The text of this Exhibit A may differ slightly from the text
+ * of the notices in the Source Code files of the Original Code. You
+ * should use the text of this Exhibit A rather than the text found in the
+ * Original Code Source Code for Your Modifications.]
+ *
+ */
+package org.mulgara.store.statement.xa11;
+
+// Java 2 standard packages
+import java.io.*;
+
+// JUnit
+import junit.framework.*;
+
+// log4j
+import org.apache.log4j.*;
+
+// locally written packages
+import org.mulgara.store.statement.*;
+import org.mulgara.util.*;
+
+
+/**
+ * Test case for {@link XA11StatementStoreImpl}.
+ *
+ * @author <a href="http://staff.pisoftware.com/david">David Makepeace</a>
+ * @author <a href="http://staff.pisoftware.com/pag">Paul Gearon</a>
+ * @copyright &copy;2001-2004 <a href="http://www.pisoftware.com/">Plugged In Software Pty Ltd</a>
+ * @licence <a href="{@docRoot}/../../LICENCE">Mozilla Public License v1.1</a>
+ */
+public class XA11StatementStoreImplUnitTest extends StatementStoreAbstractUnitTest {
+
+  /** Logger. */
+  @SuppressWarnings("unused")
+  private final static Logger logger = Logger.getLogger(XA11StatementStoreImplUnitTest.class);
+
+  /**
+   * start of filenames to build the graph with.
+   */
+  private final static String DBFILENAME = "graphtest";
+
+  /**
+   * Description of the Field
+   */
+  private XA11StatementStoreImpl xaStore;
+
+
+  /**
+   * Named constructor.
+   *
+   * @param name The name of the test.
+   */
+  public XA11StatementStoreImplUnitTest(String name) {
+    super(name);
+  }
+
+
+  /**
+   * Hook for test runner to obtain a test suite from.
+   *
+   * @return The test suite to run.
+   */
+  public static Test suite() {
+    return new TestSuite(XA11StatementStoreImplUnitTest.class);
+    //TestSuite suite = new TestSuite();
+    //suite.addTest(new GraphImplTest("testRemoveTriples"));
+    //suite.addTest(new GraphImplTest("testFindTriplesByNode0"));
+    //suite.addTest(new GraphImplTest("testFindTriplesByNode1"));
+    //suite.addTest(new GraphImplTest("testFindTriplesByNode2"));
+    //suite.addTest(new GraphImplTest("testFindTriplesByNode01"));
+    //suite.addTest(new GraphImplTest("testFindTriplesByNode02"));
+    //suite.addTest(new GraphImplTest("testFindTriplesByNode12"));
+    //return suite;
+  }
+
+
+  /**
+   * Default test runner.
+   *
+   * @param args The command line arguments
+   */
+  public static void main(String[] args) {
+
+    junit.textui.TestRunner.run(suite());
+  }
+
+
+  /**
+   * A method to call for each graph before running tests on it.
+   *
+   * @throws Exception EXCEPTION TO DO
+   */
+  protected void setUp() throws Exception {
+    boolean exceptionOccurred = true;
+    try {
+      // create the graph object, using a new file
+      store = new XA11StatementStoreImpl(
+          TempDir.getTempDir().getPath() + File.separatorChar + DBFILENAME
+      );
+      xaStore = (XA11StatementStoreImpl) store;
+      xaStore.clear();
+      super.setUp();
+      exceptionOccurred = false;
+    }
+    finally {
+      if (exceptionOccurred) {
+        tearDown();
+      }
+    }
+  }
+
+
+  /**
+   * The teardown method for JUnit
+   *
+   * @throws Exception EXCEPTION TO DO
+   */
+  protected void tearDown() throws Exception {
+    if (xaStore != null) {
+      xaStore.unmap();
+      if (System.getProperty("os.name").startsWith("Win")) {
+        // Need this for Windows or truncate() always fails for mapped files.
+        System.gc();
+        System.runFinalization();
+      }
+    }
+    super.tearDown();
+  }
+}




More information about the Mulgara-svn mailing list