[Mulgara-svn] r1626 - in trunk/src/jar: content-rlog content-rlog/java/org/mulgara/content/rlog content-rlog/java/org/mulgara/krule/rlog content-rlog/java/org/mulgara/krule/rlog/ast content-rlog/java/org/mulgara/krule/rlog/ast/output content-rlog/java/org/mulgara/krule/rlog/parser content-rlog/javacc/org/mulgara/krule/rlog krule/java/org/mulgara/krule query/java/org/mulgara/query query/java/org/mulgara/query/rdf resolver resolver/java/org/mulgara/resolver resolver-prefix/java/org/mulgara/resolver/prefix resolver-relational/java/org/mulgara/resolver/relational rules/java/org/mulgara/rules store-stringpool-xa11/java/org/mulgara/store/stringpool/xa11 util/java/org/mulgara/util/functional web/java/org/mulgara/webquery

pag at mulgara.org pag at mulgara.org
Tue Mar 24 18:42:51 UTC 2009


Author: pag
Date: 2009-03-24 11:42:49 -0700 (Tue, 24 Mar 2009)
New Revision: 1626

Added:
   trunk/src/jar/content-rlog/java/org/mulgara/content/rlog/RlogStructure.java
   trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/Program.java
   trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/CanonicalPredicate.java
   trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/CanonicalPredicateTest.java
   trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/CanonicalStatement.java
   trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/CheckRule.java
   trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/VariableCanonicalizer.java
   trunk/src/jar/krule/java/org/mulgara/krule/ConsistencyCheck.java
   trunk/src/jar/query/java/org/mulgara/query/rdf/Krule.java
Modified:
   trunk/src/jar/content-rlog/build.xml
   trunk/src/jar/content-rlog/java/org/mulgara/content/rlog/RlogContentHandler.java
   trunk/src/jar/content-rlog/java/org/mulgara/content/rlog/RlogStatements.java
   trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/Rlog.java
   trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/Axiom.java
   trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/BPredicate.java
   trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/IntegerLiteral.java
   trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/InvertedPredicate.java
   trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/NullPredicate.java
   trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/Predicate.java
   trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/PredicateLiteral.java
   trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/PredicateParam.java
   trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/Rule.java
   trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/Statement.java
   trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/StringLiteral.java
   trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/TypeStatement.java
   trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/Variable.java
   trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/output/AxiomGenerator.java
   trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/output/RuleGenerator.java
   trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/output/RuleWriter.java
   trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/output/TripleGenerator.java
   trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/parser/NSUtils.java
   trunk/src/jar/content-rlog/javacc/org/mulgara/krule/rlog/RlogParser.java
   trunk/src/jar/content-rlog/javacc/org/mulgara/krule/rlog/RlogParser.jj
   trunk/src/jar/content-rlog/javacc/org/mulgara/krule/rlog/RlogParserConstants.java
   trunk/src/jar/content-rlog/javacc/org/mulgara/krule/rlog/RlogParserTokenManager.java
   trunk/src/jar/krule/java/org/mulgara/krule/KruleLoader.java
   trunk/src/jar/krule/java/org/mulgara/krule/QueryStruct.java
   trunk/src/jar/krule/java/org/mulgara/krule/Rule.java
   trunk/src/jar/query/java/org/mulgara/query/ConstraintConjunction.java
   trunk/src/jar/query/java/org/mulgara/query/ConstraintDisjunction.java
   trunk/src/jar/query/java/org/mulgara/query/rdf/Mulgara.java
   trunk/src/jar/resolver-prefix/java/org/mulgara/resolver/prefix/PrefixResolverFactory.java
   trunk/src/jar/resolver-relational/java/org/mulgara/resolver/relational/RelationalResolverUnitTest.java
   trunk/src/jar/resolver/build.xml
   trunk/src/jar/resolver/java/org/mulgara/resolver/BuildRulesOperation.java
   trunk/src/jar/resolver/java/org/mulgara/resolver/ExportOperation.java
   trunk/src/jar/rules/java/org/mulgara/rules/DummyRuleLoader.java
   trunk/src/jar/rules/java/org/mulgara/rules/RuleLoader.java
   trunk/src/jar/rules/java/org/mulgara/rules/RuleLoaderFactory.java
   trunk/src/jar/rules/java/org/mulgara/rules/RulesRef.java
   trunk/src/jar/store-stringpool-xa11/java/org/mulgara/store/stringpool/xa11/XA11StringPoolLoadTest.java
   trunk/src/jar/util/java/org/mulgara/util/functional/C.java
   trunk/src/jar/util/java/org/mulgara/util/functional/Pair.java
   trunk/src/jar/web/java/org/mulgara/webquery/QueryServlet.java
Log:
Merged in the consistency branch. Now Krule does consistency checking

Modified: trunk/src/jar/content-rlog/build.xml
===================================================================
--- trunk/src/jar/content-rlog/build.xml	2009-03-24 01:12:37 UTC (rev 1625)
+++ trunk/src/jar/content-rlog/build.xml	2009-03-24 18:42:49 UTC (rev 1626)
@@ -60,7 +60,7 @@
   </target>
 
   <target name="content-rlog-compile"
-          depends="-content-rlog-prepare, gen-rlog-parser, resolver-spi-jar, resolver-file-jar, krule-jar"
+          depends="-content-rlog-prepare, gen-rlog-parser, resolver-spi-jar, resolver-file-jar"
           description="Compiles all content-rlog related files included generated source code">
     <javac destdir="${content-rlog.obj.dir}/classes" debug="on" deprecation="on" source="1.5" encoding="UTF-8">
       <classpath refid="content-rlog-classpath"/>

Modified: trunk/src/jar/content-rlog/java/org/mulgara/content/rlog/RlogContentHandler.java
===================================================================
--- trunk/src/jar/content-rlog/java/org/mulgara/content/rlog/RlogContentHandler.java	2009-03-24 01:12:37 UTC (rev 1625)
+++ trunk/src/jar/content-rlog/java/org/mulgara/content/rlog/RlogContentHandler.java	2009-03-24 18:42:49 UTC (rev 1626)
@@ -18,6 +18,10 @@
 package org.mulgara.content.rlog;
 
 // Java 2 enterprise packages
+import java.io.BufferedWriter;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+
 import javax.activation.MimeType;
 import javax.activation.MimeTypeParseException;
 
@@ -94,6 +98,14 @@
    */
   public void serialize(Statements statements, Content content, ResolverSession resolverSession)
           throws ContentHandlerException, ModifiedException {
-    throw new UnsupportedOperationException();
+    try {
+      Writer out = new BufferedWriter(new OutputStreamWriter(content.newOutputStream(), "utf-8"));
+      RlogStructure struct = new RlogStructure(resolverSession);
+      struct.load(statements);
+      struct.write(out);
+      out.close();
+    } catch (Exception e) {
+      throw new ContentHandlerException("Failed to serialize RLog to " + content.getURIString(), e);
+    }
   }
 }

Modified: trunk/src/jar/content-rlog/java/org/mulgara/content/rlog/RlogStatements.java
===================================================================
--- trunk/src/jar/content-rlog/java/org/mulgara/content/rlog/RlogStatements.java	2009-03-24 01:12:37 UTC (rev 1625)
+++ trunk/src/jar/content-rlog/java/org/mulgara/content/rlog/RlogStatements.java	2009-03-24 18:42:49 UTC (rev 1626)
@@ -54,7 +54,7 @@
 /**
  * Parses an {@link InputStream} into {@link Statements}.
  * This parser uses memory and does not stream.
- * 
+ *
  * @created Feb 24, 2009
  * @author Paul Gearon
  * @copyright &copy; 2008 <a href="http://www.fedora-commons.org/">Fedora Commons</a>
@@ -98,7 +98,7 @@
     try {
       InputStreamReader input = new InputStreamReader(content.newInputStream());
       try {
-        rlogParser = new Rlog(input);
+        rlogParser = new Rlog(input, content.getURI());
       } finally {
         input.close();
       }

Copied: trunk/src/jar/content-rlog/java/org/mulgara/content/rlog/RlogStructure.java (from rev 1625, branches/consistency/src/jar/content-rlog/java/org/mulgara/content/rlog/RlogStructure.java)
===================================================================
--- trunk/src/jar/content-rlog/java/org/mulgara/content/rlog/RlogStructure.java	                        (rev 0)
+++ trunk/src/jar/content-rlog/java/org/mulgara/content/rlog/RlogStructure.java	2009-03-24 18:42:49 UTC (rev 1626)
@@ -0,0 +1,853 @@
+/*
+ * 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.content.rlog;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.mulgara.krule.rlog.parser.NSUtils;
+import org.mulgara.query.TuplesException;
+import org.mulgara.query.rdf.Krule;
+import org.mulgara.query.rdf.URIReferenceImpl;
+import org.mulgara.resolver.spi.GlobalizeException;
+import org.mulgara.resolver.spi.LocalizeException;
+import org.mulgara.resolver.spi.ResolverSession;
+import org.mulgara.resolver.spi.Statements;
+import org.mulgara.util.functional.Pair;
+
+import org.apache.log4j.Logger;
+import org.jrdf.graph.Literal;
+import org.jrdf.graph.Node;
+import org.jrdf.graph.URIReference;
+import org.jrdf.vocabulary.RDF;
+
+/**
+ * This class constructs an RLog structure out of a set of Statements.
+ *
+ * @created Mar 18, 2009
+ * @author Paul Gearon
+ * @copyright &copy; 2009 <a href="http://www.fedora-commons.org/">Fedora Commons</a>
+ */
+public class RlogStructure {
+
+  /** Logger.  */
+  private static Logger logger = Logger.getLogger(RlogStructure.class.getName());
+
+  /** The rdf:type URI. */
+  private static final URIReference TYPE = new URIReferenceImpl(RDF.TYPE);
+
+  /** The rdf:value URI. */
+  private static final URIReference RDF_VALUE = new URIReferenceImpl(RDF.VALUE);
+
+  /** The rdf:Seq URI. */
+  private static final URIReference RDF_SEQ = new URIReferenceImpl(RDF.SEQ);
+
+  /** The domain for system graphs. */
+  private static final String SYS = "sys";
+
+  /** The session to use for localizing and globalizing. */
+  ResolverSession session;
+
+  /** The graph to store the statements in. */
+  Map<Node,Map<URIReference,Set<Node>>> graph = new HashMap<Node,Map<URIReference,Set<Node>>>();
+
+  /** Stores all the nodes of a particular type in a single set, one for each type. */
+  Map<Node,Set<Node>> nodesByType = new HashMap<Node,Set<Node>>();
+
+  /** Maps nodes to their type */
+  Map<Node,Node> typeByNode = new HashMap<Node,Node>();
+
+  /** Maps variables to their names */
+  Map<Node,String> nameByVar = new HashMap<Node,String>();
+
+  /** Maps namesto their variables */
+  Map<String,Node> varByName = new HashMap<String,Node>();
+
+  /** Maps all the in-use namespaces to their shorthand prefixes. */
+  Map<String,String> namespaces;
+
+  /** Default map of namespaces to their shorthand prefixes. */
+  Map<String,String> defaultNamespaces;
+
+  /** A generator for new namespace names. */
+  NamespaceGenerator namespaceGen = new NamespaceGenerator();
+
+  /** The current constraint context when writing a rule. */
+  Node currentRootConstraint = null;
+
+  /** A list of the rdf:_x IDs used for sequences. */
+  List<URIReference> listIds = new ArrayList<URIReference>();
+
+  /** The local node for the rdf:type URI. */
+  final long rdfType;
+
+
+  /**
+   * Constructs the structure, along with the session used to help build it.
+   * @param session The mechanism for convergins local nodes to global, and back.
+   * @throws LocalizeException If a global node could not be localized to a long.
+   */
+  public RlogStructure(ResolverSession session) throws LocalizeException {
+    this.session = session;
+    rdfType = session.localize(TYPE);
+    initNamespaces();
+  }
+
+
+  /**
+   * Initialized the namespaces with the registered domains.
+   */
+  private void initNamespaces() {
+    Map<String,String> ns = new HashMap<String,String>();
+    for (Map.Entry<String,String> e: NSUtils.getRegisteredDomains()) {
+      ns.put(e.getValue(), e.getKey());
+    }
+    defaultNamespaces = Collections.unmodifiableMap(ns);
+    namespaces = new HashMap<String,String>();
+    // add in sys: so it does not get mapped
+    namespaces.put(SYS + ":", SYS);
+  }
+
+
+  /**
+   * Build a structure in memory to represent the Krule data structure.
+   * @param statements The statements to load.
+   * @throws TuplesException If there was a problem accessing the statements.
+   * @throws GlobalizeException If there was a problem converting the statements to references.
+   */
+  public void load(Statements statements) throws TuplesException, GlobalizeException {
+    statements.beforeFirst();
+    while (statements.next()) {
+      long subject = statements.getSubject();
+      long predicate = statements.getPredicate();
+      long obj = statements.getObject();
+
+      if (predicate == rdfType) {
+        addType(subject, obj);
+      } else {
+        addToGraph(subject, predicate, obj);
+      }
+    }
+    nameVariables();
+  }
+
+
+  /**
+   * For every variable that was found, map it to its name.
+   */
+  void nameVariables() {
+    Set<Node> vars = nodesByType.get(Krule.VARIABLE);
+    if (vars == null) return;
+    for (Node v: vars) {
+      Node name = getPropertyValue(v, Krule.NAME);
+      if (!(name instanceof Literal)) throw new IllegalArgumentException("Bad Krule structure. Variable name is not a string.");
+      String sName = getValidVariableName((Literal)name);
+      nameByVar.put(v, sName);
+      varByName.put(sName, v);
+    }
+  }
+
+
+  /**
+   * Write a set of statements representing RLog to a given writer.
+   * @param writer The output to send the RLog to.
+   * @throws IOException If the data could not be converted or written out.
+   */
+  public void write(Writer writer) throws IOException {
+    if (writer == null) throw new IllegalArgumentException("Writer cannot be null.");
+
+    StringWriter out = new StringWriter();
+    writeAxioms(out);
+    writeChecks(out);
+    writeRules(out);
+    out.close();
+
+    writePrefixes(writer);
+    writer.append(out.getBuffer());
+  }
+
+
+  /**
+   * Sets the type for an object.
+   * @param s The subject to be typed.
+   * @param t The type of the subject.
+   * @throws GlobalizeException If the subject or type could not be globalized.
+   */
+  void addType(long s, long t) throws GlobalizeException {
+    Node subj = session.globalize(s);
+    Node type = session.globalize(t);
+    // map the type to the node
+    addValue(nodesByType, type, subj);
+    // map the node to its type
+    typeByNode.put(subj, type);
+  }
+
+
+  /**
+   * Add a triple to the graph.
+   * @param s The subject of the triple.
+   * @param p The predicate of the triple.
+   * @param o The object of the triple.
+   * @throws GlobalizeException If one of the elements of the triple could not be
+   *         converted to a global node.
+   */
+  void addToGraph(long s, long p, long o) throws GlobalizeException {
+    Node subj = session.globalize(s);
+    URIReference pred = (URIReference)session.globalize(p);
+    Node obj = session.globalize(o);
+    addPropertyValue(graph, subj, pred, obj);
+  }
+
+
+  /**
+   * Writes all the axioms to the output as horn clauses.
+   * @param out The writer to send the RLog to.
+   * @throws IOException If there was an error writing to the output.
+   */
+  void writePrefixes(Writer out) throws IOException {
+    for (Map.Entry<String,String> ns: namespaces.entrySet()) {
+      String k = ns.getKey();
+      String v = ns.getValue() + ":";
+      if (v.equals(k)) continue;
+      out.append("@prefix ");
+      out.append(v);
+      out.append(" <");
+      out.append(k);
+      out.append("> .\n");
+    }
+    out.append("\n");
+  }
+
+
+  /**
+   * Writes all the axioms to the output as horn clauses.
+   * @param out The writer to send the RLog to.
+   * @throws IOException If there was an error writing to the output.
+   */
+  void writeAxioms(Writer out) throws IOException {
+    Set<Node> axioms = nodesByType.get(Krule.AXIOM);
+    if (axioms == null) return;
+    for (Node axiom: axioms) {
+      Map<URIReference,Set<Node>> properties = graph.get(axiom);
+      Node s = getSingle(properties, Krule.AXIOM_SUBJECT);
+      Node p = getSingle(properties, Krule.AXIOM_PREDICATE);
+      Node o = getSingle(properties, Krule.AXIOM_OBJECT);
+      out.append(toPredicate(s, p, o));
+      out.append(".\n");
+    }
+    out.append("\n");
+  }
+
+
+  /**
+   * Writes all the consistency checks to the output as horn clauses.
+   * @param out The writer to send the RLog to.
+   * @throws IOException If there was an error writing to the output.
+   */
+  void writeChecks(Writer out) throws IOException {
+    Set<Node> checks = nodesByType.get(Krule.CHECK);
+    if (checks == null) return;
+    for (Node check: checks) {
+      Map<URIReference,Set<Node>> properties = graph.get(check);
+      Node q = getSingle(properties, Krule.HAS_QUERY);
+      Node qType = typeByNode.get(q);
+      if (!qType.equals(Krule.QUERY)) {
+        throw new IllegalArgumentException("Bad Krule structure. Consistency check has a query that has a non-query type: " + qType);
+      }
+      out.append(":- ");
+      writeBody(out, q);
+      out.append(".\n");
+    }
+    out.append("\n");
+  }
+
+
+  /**
+   * Writes all the rules to the output as horn clauses.
+   * @param out The writer to send the RLog to.
+   * @throws IOException If there was an error writing to the output.
+   */
+  void writeRules(Writer out) throws IOException {
+    Set<Node> rules = nodesByType.get(Krule.RULE);
+    if (rules == null) return;
+    for (Node rule: rules) {
+      Node q = getPropertyValue(rule, Krule.HAS_QUERY);
+      Node qType = typeByNode.get(q);
+      if (!qType.equals(Krule.QUERY)) {
+        throw new IllegalArgumentException("Bad Krule structure. Consistency check has a query that has a non-query type: " + qType);
+      }
+      writeHead(out, q);
+      out.append(" :- ");
+      writeBody(out, q);
+      out.append(".\n");
+    }
+    out.append("\n");
+  }
+
+
+  /**
+   * Writes out the head of a query associated with a rule.
+   * @param out The writer output.
+   * @param query The node representing the query.
+   */
+  void writeHead(Writer out, Node query) throws IOException {
+    Node seq = getPropertyValue(query, Krule.SELECTION_VARS);
+    Node sType = typeByNode.get(seq);
+    if (!sType.equals(RDF_SEQ)) {
+      throw new IllegalArgumentException("Bad Krule structure. Query selection sequence is not a sequence type: " + sType);
+    }
+
+    // loop over multiple triples
+    int seqNr = 1;
+    Node s;
+    while (null != (s = getPropertyValue(seq, getListId(seqNr++)))) {
+      Node p = getPropertyValue(seq, getListId(seqNr++));
+      Node o = getPropertyValue(seq, getListId(seqNr++));
+      if (p == null || o == null) throw new IllegalArgumentException("Bad Krule structure. Query selection sequence is not set of triples. " + seqNr + " elements");
+      // separate triples with commas
+      if (seqNr > 4) out.append(", ");
+      out.append(toPredicate(s, p, o));
+    }
+  }
+
+
+  /**
+   * Writes out the body (WHERE clause) of a query associated with a rule.
+   * @param out The writer output.
+   * @param query The node representing the query.
+   */
+  void writeBody(Writer out, Node query) throws IOException {
+    Node constraint = getPropertyValue(query, Krule.HAS_WHERE_CLAUSE);
+    // remember the current context in case it is needed
+    currentRootConstraint = constraint;
+    writeConstraint(out, constraint, false);
+  }
+
+
+  /**
+   * Writes a general constraint type that may be inverted. This method is recursive.
+   * @param out The writer output.
+   * @param constraint The constraint to be written.
+   *        Accepts conjunctions, differences, transitive and simple constraints.
+   * @param inv Indicates that the constraint is inverted.
+   * @throws IOException Due to a write error on the writer.
+   */
+  void writeConstraint(Writer out, Node constraint, boolean inv) throws IOException {
+    Node cType = typeByNode.get(constraint);
+    if (cType.equals(Krule.CONSTRAINT_CONJUNCTION)) {
+      writeConstraintConjunction(out, constraint, inv);
+    } else if (cType.equals(Krule.SIMPLE_CONSTRAINT)) {
+      writeConstraintSimple(out, constraint, inv);
+    } else if (cType.equals(Krule.DIFFERENCE)) {
+      writeConstraintDifference(out, constraint, inv);
+    } else if (cType.equals(Krule.TRANSITIVE_CONSTRAINT)) {
+      writeConstraintTransitive(out, constraint, inv);
+    } else {
+      throw new IllegalArgumentException("Bad Krule structure. Unsupported Constraint type: " + cType);
+    }
+  }
+
+
+  /**
+   * Writes the simple constraint type. This is just a triple.
+   * @param out The writer output.
+   * @param constraint The constraint to be written.
+   * @param inv Indicates that the constraint is inverted.
+   * @throws IOException Due to a write error on the writer.
+   */
+  void writeConstraintSimple(Writer out, Node constraint, boolean inv) throws IOException {
+    Node s = getPropertyValue(constraint, Krule.HAS_SUBJECT);
+    Node p = getPropertyValue(constraint, Krule.HAS_PREDICATE);
+    Node o = getPropertyValue(constraint, Krule.HAS_OBJECT);
+    // throw away the graph, as this is autodetected from the predicate in RLog parsing
+    if (s == null || p == null || o == null) {
+      throw new IllegalArgumentException("Bad Krule structure. Incomplete constraint.");
+    }
+    if (inv) out.append("~");
+    out.append(toPredicate(s, p, o));
+  }
+
+
+  /**
+   * Writes a constraint conjunction.
+   * @param out The writer output.
+   * @param constraint The constraint to be written.
+   * @param inv Indicates that the constraint is inverted.
+   * @throws IOException Due to a write error on the writer.
+   */
+  void writeConstraintConjunction(Writer out, Node constraint, boolean inv) throws IOException {
+    Map<URIReference,Set<Node>> constraintProps = graph.get(constraint);
+    if (constraintProps == null) throw new IllegalArgumentException("Bad Krule structure. Missing arguments for a conjunction.");
+    Set<Node> args = constraintProps.get(Krule.ARGUMENT);
+    // go through all the constraints, separating them with commas
+    boolean first = true;
+    for (Node c: args) {
+      if (first) first = false;
+      else out.append(", ");
+      writeConstraint(out, c, inv);
+    }
+  }
+
+
+  /**
+   * Writes a difference constraint.
+   * @param out The writer output.
+   * @param constraint The constraint to be written.
+   * @param inv Indicates that the constraint is inverted.
+   * @throws IOException Due to a write error on the writer.
+   */
+  void writeConstraintDifference(Writer out, Node constraint, boolean inv) throws IOException {
+    Node minuend = getPropertyValue(constraint, Krule.MINUEND);
+    Node subtrahend = getPropertyValue(constraint, Krule.SUBTRAHEND);
+    if (minuend == null) throw new IllegalArgumentException("Bad Krule structure. Missing minuend on a Difference.");
+    if (subtrahend == null) throw new IllegalArgumentException("Bad Krule structure. Missing subtrahend on a Difference.");
+    writeConstraint(out, minuend, inv);
+    out.append(", ");
+    writeConstraint(out, subtrahend, !inv);
+  }
+
+
+  /**
+   * Writes a transitive constraint.
+   * @param out The writer output.
+   * @param constraint The constraint to be written.
+   * @param inv Indicates that the constraint is inverted.
+   * @throws IOException Due to a write error on the writer.
+   */
+  void writeConstraintTransitive(Writer out, Node constraint, boolean inv) throws IOException {
+    Node arg = getPropertyValue(constraint, Krule.TRANSITIVE_ARGUMENT);
+    if (arg == null || !arg.equals(Krule.SIMPLE_CONSTRAINT)) {
+      throw new IllegalArgumentException("Bad Krule structure. Transitive constraints must operate on simple arguments.");
+    }
+    Node s = getPropertyValue(arg, Krule.HAS_SUBJECT);
+    Node p = getPropertyValue(arg, Krule.HAS_PREDICATE);
+    Node o = getPropertyValue(arg, Krule.HAS_OBJECT);
+    // throw away the graph, as this is autodetected from the predicate in RLog parsing
+    if (s == null || p == null || o == null) {
+      throw new IllegalArgumentException("Bad Krule structure. Incomplete constraint in transitive constraint.");
+    }
+
+    Node newVar = getUnusedVar();
+    out.append(toPredicate(s, p, newVar));
+    out.append(", ");
+    out.append(toPredicate(newVar, p, o));
+  }
+
+
+  /**
+   * Finds a variable that is not in use in the current constraint.
+   * @return A variable this is not being used in the current rule.
+   */
+  Node getUnusedVar() {
+    for (char v = 'A'; v <= 'Z'; v++) {
+      Node var = varByName.get(Character.toString(v));
+      if (var != null) return var;
+    }
+    // every variable is in use somewhere. Have to search the current rule.
+    Set<Node> currentVariables = getVariables(currentRootConstraint);
+    for (Node var: nameByVar.keySet()) {
+      if (!currentVariables.contains(var)) return var;
+    }
+    throw new IllegalStateException("Rule is too complex. It contains too many variables.");
+  }
+
+
+  /**
+   * Accumulates all of the variables under a given node.
+   * @param constraint The constraint node to find variables under.
+   * @return A Set of nodes which represent variables.
+   */
+  Set<Node> getVariables(Node constraint) {
+    Node type = typeByNode.get(constraint);
+    if (type.equals(Krule.SIMPLE_CONSTRAINT)) {
+      return getVariablesSimple(constraint);
+    } else if (type.equals(Krule.CONSTRAINT_CONJUNCTION)) {
+      return getVariablesConjunction(constraint);
+    } else if (type.equals(Krule.DIFFERENCE)) {
+      return getVariablesDifference(constraint);
+    } else if (type.equals(Krule.TRANSITIVE_CONSTRAINT)) {
+      return getVariablesTransitive(constraint);
+    } else {
+      throw new IllegalArgumentException("Bad Krule structure. Unsupported Constraint type: " + type);
+    }
+  }
+
+
+  /**
+   * Accumulates all of the variables in a simple constraint.
+   * @param constraint The constraint node to find variables under.
+   * @return A Set of nodes which represent variables.
+   */
+  Set<Node> getVariablesSimple(Node constraint) {
+    Node s = getPropertyValue(constraint, Krule.HAS_SUBJECT);
+    Node p = getPropertyValue(constraint, Krule.HAS_PREDICATE);
+    Node o = getPropertyValue(constraint, Krule.HAS_OBJECT);
+    if (s == null || p == null || o == null) {
+      throw new IllegalArgumentException("Bad Krule structure. Incomplete constraint.");
+    }
+    Set<Node> vars = new HashSet<Node>();
+    if (nameByVar.containsKey(s)) vars.add(s);
+    if (nameByVar.containsKey(p)) vars.add(p);
+    if (nameByVar.containsKey(o)) vars.add(o);
+    return vars;
+  }
+
+
+  /**
+   * Accumulates all of the variables in a conjunction.
+   * @param constraint The constraint node to find variables under.
+   * @return A Set of nodes which represent variables.
+   */
+  Set<Node> getVariablesConjunction(Node constraint) {
+    // Recursively get the variables from the arguments
+    Map<URIReference,Set<Node>> constraintProps = graph.get(constraint);
+    if (constraintProps == null) throw new IllegalArgumentException("Bad Krule structure. Missing arguments for a conjunction.");
+    Set<Node> args = constraintProps.get(Krule.ARGUMENT);
+    // accumulate the variables into the first result. This is not functional, but more efficient.
+    Set<Node> vars = null;
+    for (Node c: args) {
+      Set<Node> tmp = getVariables(c);
+      if (vars == null) vars = tmp;
+      else vars.addAll(tmp);
+    }
+    return vars;
+  }
+
+
+  /**
+   * Accumulates all of the variables in a difference.
+   * @param constraint The constraint node to find variables under.
+   * @return A Set of nodes which represent variables.
+   */
+  Set<Node> getVariablesDifference(Node constraint) {
+    // Recursively get the variables from the arguments
+    Node minuend = getPropertyValue(constraint, Krule.MINUEND);
+    Node subtrahend = getPropertyValue(constraint, Krule.SUBTRAHEND);
+    if (minuend == null) throw new IllegalArgumentException("Bad Krule structure. Missing minuend on a Difference.");
+    if (subtrahend == null) throw new IllegalArgumentException("Bad Krule structure. Missing subtrahend on a Difference.");
+    Set<Node> vars = getVariables(minuend);
+    vars.addAll(getVariables(subtrahend));  // not quite functional, but more efficient
+    return vars;
+  }
+
+
+  /**
+   * Accumulates all of the variables in a transitive constraint.
+   * @param constraint The constraint node to find variables under.
+   * @return A Set of nodes which represent variables.
+   */
+  Set<Node> getVariablesTransitive(Node constraint) {
+    // Recursively get the variables from the arguments
+    Node arg = getPropertyValue(constraint, Krule.TRANSITIVE_ARGUMENT);
+    if (arg == null || !arg.equals(Krule.SIMPLE_CONSTRAINT)) {
+      throw new IllegalArgumentException("Bad Krule structure. Transitive constraints must operate on simple arguments.");
+    }
+    return getVariables(arg);
+  }
+
+
+  /**
+   * Converts a triple to a string containing a DL predicate.
+   * @param s The subject of the triple.
+   * @param p The predicate of the triple.
+   * @param o The object of the triple.
+   * @return A string with the DL version of the predicate.
+   */
+  String toPredicate(Node s, Node p, Node o) {
+    if (isType(p)) return toTypePredicate(s, o);
+    return toBinaryPredicate(s, p, o);
+  }
+
+
+  /**
+   * Convert a subject and type into a Type predicate.
+   * @param s The subject to be typed.
+   * @param type The type of the subject.
+   * @return A string with the subject and type.
+   */
+  String toTypePredicate(Node s, Node type) {
+    StringBuilder sb = new StringBuilder();
+    sb.append(toString(type));
+    sb.append("(");
+    sb.append(toString(s));
+    sb.append(")");
+    return sb.toString();
+  }
+
+
+  /**
+   * Convert a triple into a binary predicate.
+   * @param s The subject of the triple.
+   * @param p The predicate of the triple.
+   * @param o The object of the triple.
+   * @return A string with the DL version of the binary predicate.
+   */
+  String toBinaryPredicate(Node s, Node p, Node o) {
+    StringBuilder sb = new StringBuilder();
+    sb.append(toString(p));
+    sb.append("(");
+    sb.append(toString(s));
+    sb.append(",");
+    sb.append(toString(o));
+    sb.append(")");
+    return sb.toString();
+  }
+
+
+  /**
+   * Gets the string representation of an object referenced by URIReference.
+   * @param n The node to get the value of.
+   * @return A string representation of the object.
+   */
+  String toString(Node n) {
+    Node refType = typeByNode.get(n);  // Variable, URIReference or Literal
+    if (refType == null) throw new IllegalArgumentException("Bad Krule structure. No type for node: " + n);
+    if (!(refType instanceof URIReference)) throw new IllegalArgumentException("Bad Krule structure. Expected a reference for type, but got: " + refType);
+
+    // Get the referenced data
+    if (refType.equals(Krule.URI_REF)) {
+      // writing a URI
+      Node value = getPropertyValue(n, RDF_VALUE);
+      if (value == null || !(value instanceof URIReference)) throw new IllegalArgumentException("Bad Krule structure. URIReference came back with the wrong type: " + value + "(" + value.getClass().getName() + ")");
+      return namespaceString((URIReference)value);
+
+    } else if (refType.equals(Krule.VARIABLE)) {
+      return nameByVar.get(n);
+
+    } else if (refType.equals(Krule.LITERAL)) {
+      // writing a literal
+      Node value = getPropertyValue(n, RDF_VALUE);
+      if (!(value instanceof Literal)) throw new IllegalArgumentException("Bad Krule structure. Literal came back with the wrong type: " + value + "(" + value.getClass().getName() + ")");
+      return value.toString();
+
+    } else throw new IllegalArgumentException("Bad Krule structure. Output node is not a URI Reference, a variable, or a literal: " + refType);
+  }
+
+
+  /**
+   * Tests if a node is a reference to the rdf:type URI.
+   * @param n The node to check as a reference.
+   * @return <code>true</code> only if the node is a reference and it refers to rdf:type
+   */
+  boolean isType(Node n) {
+    Node refType = typeByNode.get(n);  // Variable, URIReference or Literal
+    if (refType == null) throw new IllegalArgumentException("Bad Krule structure. No type for node: " + n);
+    if (!(refType instanceof URIReference)) throw new IllegalArgumentException("Bad Krule structure. Expected a reference for type, but got: " + refType);
+
+    // Get the referenced data
+    if (refType.equals(Krule.URI_REF)) {
+      // writing a URI
+      Node value = getPropertyValue(n, RDF_VALUE);
+      if (value == null || !(value instanceof URIReference)) throw new IllegalArgumentException("Bad Krule structure. URIReference came back with the wrong type: " + value + "(" + value.getClass().getName() + ")");
+      return value.equals(TYPE);
+    }
+    return false;
+  }
+
+
+  /**
+   * Get the namespace version of a URI.
+   * e.g. rdf:value instead of http://www.w3.org/1999/02/22-rdf-syntax-ns#value
+   * @param r The URI reference to convert to namespace form.
+   * @return The namespaced version of the URI.
+   */
+  String namespaceString(URIReference r) {
+    Pair<String,String> nsPair = addToNamespace(r);
+    String dom = nsPair.first();
+    String val = nsPair.second();
+
+    // Krule is the default namespace
+    if (dom.equals(Krule.KRULE)) return val;
+    return ((dom.endsWith(":")) ? dom : dom + ":") + val;
+  }
+
+
+  /**
+   * Scans nodes that are URIReferences, and adds them to the list of namespaces if not already there.
+   * @param r A node that may be a reference to be scanned.
+   */
+  void addToNamespace(Node n) {
+    if (!(n instanceof URIReference)) return;
+    addToNamespace((URIReference)n);
+  }
+
+
+  /**
+   * Scans a URIReference, and adds it to the list of namespaces if not already there.
+   * @param r The reference to be scanned.
+   * @return The domain abbreviation to use for this reference, and the value in the domain as a Pair.
+   */
+  Pair<String,String> addToNamespace(URIReference r) {
+    Pair<String,String> nsPair = splitUri(r.getURI());
+    String ns = nsPair.first();
+    String val = nsPair.second();
+    if (val.length() == 0) throw new IllegalArgumentException("Bad RLog data. URI needs a value in a domain to be serialized in RLog: " + r);
+
+    // determine the domain to use for this namespace
+    String dom = defaultNamespaces.get(ns);
+    if (dom == null) {
+      dom = namespaces.get(ns);
+      if (dom == null) {
+        namespaces.put(ns, dom = namespaceGen.newNamespace());
+      }
+    } else {
+      // domain is in the default list, add it to the in-use list
+      if (!namespaces.containsKey(ns)) namespaces.put(ns, dom);
+    }
+    return new Pair<String,String>(dom, val);
+  }
+
+
+  /**
+   * Gets the reference for a sequence member predicate.
+   * @param n The sequence index.
+   * @return The URI reference for <em>rdf:_n</em>.
+   */
+  URIReference getListId(int n) {
+    if (listIds.size() < n) {
+      for (int i = listIds.size() + 1; i <= n; i++) {
+        URI u = URI.create(RDF.BASE_URI + "_" + i);
+        listIds.add(new URIReferenceImpl(u));
+      }
+    }
+    return listIds.get(n - 1);
+  }
+
+
+  /**
+   * Convert a literal into a valid variable name. Variable names are a single upper case character.
+   * @param value The literal to convert.
+   * @return The validated, and converted name of the variable.
+   */
+  String getValidVariableName(Literal value) {
+    String var = ((Literal)value).getLexicalForm();
+    if (var.length() != 1) {
+      String v = var.substring(0, 1).toUpperCase();
+      logger.warn("Krule structure uses long variable names. Truncating: " + var + " -> " + v);
+      var = v;
+    }
+    if (!Character.isLetter(var.charAt(0))) throw new IllegalArgumentException("Bad Krule structure. Variable must be a letter: " + var);
+    if (!Character.isUpperCase(var.charAt(0))) {
+      logger.warn("Variable names must have upper case letters: " + var);
+      var = var.toUpperCase();
+    }
+    return var;
+  }
+
+  
+  /**
+   * Gets the value of a property on an object.
+   * @param s The object in the graph.
+   * @param p The property being looked for.
+   * @return The value of the required property.
+   */
+  Node getPropertyValue(Node s, URIReference p) {
+    Map<URIReference,Set<Node>> values = graph.get(s);
+    if (values == null) throw new IllegalArgumentException("Bad Krule structure. Missing property <" + p + "> on object <" + s + ">");
+    return getSingle(values, p);
+  }
+
+
+  /**
+   * Map an object to a property value pair.
+   * @param subj The subject to set a property and value for.
+   * @param property The property for the subject.
+   * @param value The value for the subject's property.
+   */
+  static final void addPropertyValue(Map<Node,Map<URIReference,Set<Node>>> graph, Node subj, URIReference property, Node value) {
+    Map<URIReference,Set<Node>> propVal = graph.get(subj);
+    if (propVal == null) {
+      propVal = new HashMap<URIReference,Set<Node>>();
+      graph.put(subj, propVal);
+    }
+    addValue(propVal, property, value);
+  }
+
+
+  /**
+   * Map a property to a value.
+   * @param propertyValues The full set of properties and values for the current object.
+   * @param property The property to set on the current object.
+   * @param value The value to set the property to.
+   */
+  static final <T> void addValue(Map<T,Set<Node>> propertyValues, T property, Node value) {
+    Set<Node> values = propertyValues.get(property);
+    if (values == null) {
+      values = new HashSet<Node>();
+      propertyValues.put(property, values);
+    }
+    values.add(value);
+  }
+
+
+  /**
+   * Retrieves a single value from a multimap.
+   * @param <K> The type of the keys in the multimap.
+   * @param <V> The type of the values in the multimap.
+   * @param map The map to get the value from.
+   * @param key The key to find the value with.
+   * @return The single value found associated with the key, or null if not found.
+   * @throws IllegalArgumentException If the key maps to a set with more than one value.
+   */
+  static final <K,V> V getSingle(Map<K,Set<V>> map, K key) {
+    Set<V> vals = map.get(key);
+    if (vals == null || vals.size() == 0) return null;
+    if (vals.size() != 1) throw new IllegalArgumentException("Expecting singleton from a set of size: " + vals.size());
+    return vals.iterator().next();
+  }
+
+
+  /**
+   * Splits a URI into two sections. The second section contains the trailing identifier
+   * that includes only letters, digits and underscores. The first section is everything else. 
+   * @param uri The URI to split.
+   * @return A pair of strings containing the two parts of the original URI.
+   */
+  static final Pair<String,String> splitUri(URI uri) {
+    String u = uri.toString();
+    for (int p = u.length() - 1; p >= 0; p--) {
+      char ch = u.charAt(p);
+      if (!Character.isLetterOrDigit(ch) && ch != '_' && ch != '-') {
+        p = p + 1;
+        return new Pair<String,String>(u.substring(0, p), u.substring(p));
+      }
+    }
+    return new Pair<String,String>("", u);
+  }
+
+
+  /**
+   * This class is a utility to generate new namespace names.
+   */
+  class NamespaceGenerator {
+    /** The namespace counter */
+    int n = 1;
+
+    /**
+     * Create a new name for a namespace.
+     * @return A new name.
+     */
+    String newNamespace() {
+      return "ns" + n++;
+    }
+  }
+}

Copied: trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/Program.java (from rev 1625, branches/consistency/src/jar/content-rlog/java/org/mulgara/krule/rlog/Program.java)
===================================================================
--- trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/Program.java	                        (rev 0)
+++ trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/Program.java	2009-03-24 18:42:49 UTC (rev 1626)
@@ -0,0 +1,152 @@
+/*
+ * 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.krule.rlog;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+import org.mulgara.krule.rlog.ast.CanonicalStatement;
+import org.mulgara.krule.rlog.ast.Statement;
+
+/**
+ * Represents a complete program, including annotations.
+ * A program is mostly a list of statements, but can also include other elements
+ * such as &quot;imports&quot; of other programs.
+ *
+ * @created Mar 3, 2009
+ * @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 Program {
+
+  /** Logger. */
+  private static final Logger logger = Logger.getLogger(Program.class.getName());
+
+  /** All the statement that make up the program */
+  private List<Statement> statements;
+
+  /** The canonical forms of the statements that make up the program */
+  private List<CanonicalStatement> canonicalStatements;
+
+  /** The list of imports */
+  private List<URI> imports;
+
+  /**
+   * Constructs an empty program.
+   */
+  public Program() {
+    statements = new ArrayList<Statement>();
+    canonicalStatements = new ArrayList<CanonicalStatement>();
+    imports = new ArrayList<URI>();
+  }
+
+
+  /**
+   * Retrieve the statements from this program.
+   * @return A list of statements in the program.
+   */
+  public List<Statement> getStatements() {
+    return statements;
+  }
+
+
+  /**
+   * Merges the statements from another program into this one.
+   * Note that the parsing context of the foreign statements is maintained.
+   * @param p The foreign program to merge.
+   */
+  public void merge(Program p) {
+    logger.debug("Merging programs");
+    for (Statement s: p.getStatements()) add(s);
+  }
+
+
+  /**
+   * Adds a statement to this program. If the statement is already present it is skipped.
+   * @param s The statement to add.
+   */
+  public void add(Statement s) {
+    CanonicalStatement canonical = s.getCanonical();
+    if (!statementPresent(s, canonical)) {
+      statements.add(s);
+      canonicalStatements.add(canonical);
+    } else {
+      logger.debug("Not adding: " + s);
+    }
+  }
+
+
+  /**
+   * Adds an import directive to the program.
+   */
+  public void addImport(String imp) throws ParseException {
+    try {
+      URI u = new URI(imp);
+      imports.add(u);
+    } catch (URISyntaxException e) {
+      throw new ParseException("Bad URL in import");
+    }
+  }
+
+
+  /**
+   * Get all the import URIs for the document
+   * @return A List of URIs. These can be absolute or relative.
+   */
+  public List<URI> getImports() {
+    return imports;
+  }
+
+
+  /**
+   * Get an iterator for the statements in this program.
+   * @return A new iterator that returns statements.
+   */
+  public Iterator<Statement> stmtIterator() {
+    return statements.iterator();
+  }
+
+  /** @see java.lang.Object#toString() */
+  public String toString() {
+    StringBuilder sb = new StringBuilder();
+    for (Statement s: statements) {
+      sb.append(s).append("\n");
+    }
+    return sb.toString();
+  }
+
+  /**
+   * Tests if a statement is already present in the program.
+   * Attempts to canonicalize statements for comparison.
+   * @param stmt The statement to look for. Included for logging purposes.
+   * @param canonical The canonicalized form of the statement to look for.
+   * @return <code>true</code> if an equivalent statement is found, <code>false</code> otherwise.
+   */
+  private boolean statementPresent(Statement stmt, CanonicalStatement canonical) {
+    if (logger.isDebugEnabled()) logger.debug("Testing for presence of: " + stmt);
+    for (CanonicalStatement s: canonicalStatements) {
+      if (canonical.equals(s)) {
+        if (logger.isDebugEnabled()) logger.debug(canonical.toString() + " == " + s);
+        return true;
+      }
+      if (logger.isDebugEnabled()) logger.debug(canonical.toString() + " != " + s);
+    }
+    if (logger.isDebugEnabled()) logger.debug("New statement: " + stmt);
+    return false;
+  }
+}

Modified: trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/Rlog.java
===================================================================
--- trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/Rlog.java	2009-03-24 01:12:37 UTC (rev 1625)
+++ trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/Rlog.java	2009-03-24 18:42:49 UTC (rev 1626)
@@ -17,6 +17,9 @@
 package org.mulgara.krule.rlog;
 
 import java.io.*;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URL;
 import java.util.Collections;
 import java.util.LinkedHashSet;
 import java.util.List;
@@ -24,8 +27,8 @@
 import java.util.Collection;
 import java.util.Set;
 
+import org.apache.log4j.Logger;
 import org.mulgara.krule.rlog.ast.*;
-import org.mulgara.krule.rlog.ast.output.KruleWriter;
 import org.mulgara.krule.rlog.parser.TypeException;
 import org.mulgara.krule.rlog.parser.URIParseException;
 import org.mulgara.krule.rlog.rdf.MulgaraGraphs;
@@ -41,6 +44,9 @@
  */
 public class Rlog implements Interpreter {
 
+  /** Logger. */
+  private static final Logger logger = Logger.getLogger(Rlog.class.getName());
+
   /** The label used on all generated rules. */
   private static String ruleLabel = "rule";
 
@@ -54,45 +60,6 @@
   private List<Rule> rules;
 
   /**
-   * Parse input data into an AST and process.
-   * @param args May contain a filename with the code to be parsed.  All other parameters ignored.
-   * @throws IOException If there was an error reading from the specified file, or standard in.
-   * @throws beaver.Parser.Exception If there was a parsing exception.
-   */
-  public static void main(String[] args) throws IOException, ParseException {
-    // get the input
-    Reader intReader;
-    if (args.length > 0) {
-      File inFile = new File(args[0]);
-      if (!inFile.exists() || !inFile.canRead()) {
-        System.err.println("Unable to open file: " + inFile);
-        return;
-      }
-      // borrow the filename for labelling rules
-      intReader = new FileReader(inFile);
-      String filename = inFile.getName();
-      ruleLabel = filename.substring(0, filename.lastIndexOf('.'));
-    } else {
-      intReader = new InputStreamReader(System.in);
-    }
-    Reader input = new BufferedReader(intReader);
-
-    try {
-      // parse the input
-      Rlog rlog = new Rlog(input);
-      // emit to output
-      new KruleWriter(rlog).emit(System.out);
-    } catch (TypeException te) {
-      System.err.println(te.getMessage());
-      return;
-    } catch (URIParseException e) {
-      System.err.println(e.getMessage());
-      return;
-    }
-
-  }
-
-  /**
    * Sets the label to use for each rule. The rule names are made up of this label
    * plus an incremening number.
    * @param label The text label to use on rules.
@@ -104,16 +71,15 @@
   /**
    * Create an rlog interpreter for building an AST from a stream Reader object.
    * @param input The stream Reader.
+   * @param inputLocation The location the input comes from.
    * @throws IOException There was an IO Exception on the input.
    * @throws beaver.Parser.Exception There was a parser exception in the input data.
    * @throws URIParseException If the rules contain illegal URIs.
    */
-  public Rlog(Reader input) throws IOException, ParseException, TypeException, URIParseException {
-    // parse the rlog into statements
-    RlogParser parser = new RlogParser(input);
+  public Rlog(Reader input, URI inputLocation) throws IOException, ParseException, TypeException, URIParseException {
+    Program program = loadProgram(input, inputLocation);
+    statements = program.getStatements();
 
-    statements = parser.statements();
-
     // separate out the rules from the axioms
     int ruleCount = 0;
     rules = new ArrayList<Rule>();
@@ -130,7 +96,35 @@
     calculateRuleDependencies();
   }
 
+
   /**
+   * Loads a program from an input Reader.
+   * @param input The Reader to get the program from.
+   * @param baseLocation The location context to load the program in. This is used for relative
+   *        import declartations.
+   * @return The fully loaded program, including imports.
+   * @throws IOException Due to an IO error while loading the program.
+   * @throws ParseException If the program contains an error.
+   */
+  private Program loadProgram(Reader input, URI baseLocation) throws IOException, ParseException {
+    // parse the rlog into statements
+    RlogParser parser = new RlogParser(input);
+
+    try {
+      Program program = parser.getProgram();
+      loadImports(program, baseLocation);
+      return program;
+    } catch (ParseException e) {
+      logger.error("Error parsing program: " + e.getMessage());
+      throw e;
+    } catch (IOException e) {
+      logger.error("IO Error reading program: " + e.getMessage());
+      throw e;
+    }
+  }
+
+
+  /**
    * Find all the variables in every rule.
    * @return A complete collection of all the variables that were parsed.
    */
@@ -140,6 +134,7 @@
     return vars;
   }
 
+
   /**
    * Gets all the URIs referenced in the rules.
    * @return All URIs in order of appearance within axioms, then rules.
@@ -153,6 +148,7 @@
     return refs;
   }
 
+
   /**
    * Get all the axioms appearing in the rule set.
    * @return A list of axioms.
@@ -161,6 +157,7 @@
     return Collections.unmodifiableList(axioms);
   }
 
+
   /**
    * Get all the rules appearing in the rule set.
    * @return A list of rules.
@@ -169,6 +166,7 @@
     return Collections.unmodifiableList(rules);
   }
 
+
   /**
    * Determine which rules are dependent on the result of which other rules,
    * and set the rule objects accordingly.
@@ -182,4 +180,53 @@
     }
   }
 
+
+  /**
+   * Load all imports into the given program.
+   * @param prog The program to load its imports into.
+   * @param currentLocation A URI for the location of the program. Used for relative loads.
+   * @throws IOException If the import files cannot be read.
+   * @throws ParseException If an imported program has an error.
+   */
+  private void loadImports(Program prog, URI currentLocation) throws IOException, ParseException {
+    List<URI> imports = prog.getImports();
+    for (URI imp: imports) {
+      URL importLocation = makeAbsolute(currentLocation, imp);
+      if (logger.isDebugEnabled()) logger.debug("Importing " + importLocation);
+      Reader input = new InputStreamReader(importLocation.openStream());
+      Program impProgram;
+      try {
+        impProgram = loadProgram(input, currentLocation);
+      } catch (ParseException e) {
+        logger.error("Error in imported program <" + importLocation + ">");
+        throw e;
+      } finally {
+        input.close();
+      }
+      prog.merge(impProgram);
+    }
+  }
+
+
+  /**
+   * Calculate a URL from a given URI, relative to the current base location.
+   * @param base The base to calculate the final URL from. Must be a URL if <var>rel</var> is relative.
+   * @param rel The URL that is relative to the base. If absolute, then <var>base</var> is ignored.
+   * @return A new URL, which is <var>rel</var> if it is absolute, or a merge of <var>rel</var> and
+   *         <var>base</var> if <var>rel</var> is relative.
+   * @throws IllegalArgumentException If <var>rel</var> is relative and <var>base</var> is not a URL,
+   *         or if <var>rel</var> (or <var>base</var>+<var>rel</var>) is not a valid URL.
+   */
+  private URL makeAbsolute(URI base, URI rel) {
+    try {
+      if (!rel.isAbsolute()) {
+        if (base == null) throw new IllegalArgumentException("Relative URL used in import with no current location set");
+        return new URL(base.toURL(), rel.toString());
+      } else {
+        return rel.toURL();
+      }
+    } catch (MalformedURLException e) {
+      throw new IllegalArgumentException("Invalid URL for import of (" + rel + ")" + ": "+ e.getMessage());
+    }
+  }
 }

Modified: trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/Axiom.java
===================================================================
--- trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/Axiom.java	2009-03-24 01:12:37 UTC (rev 1625)
+++ trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/Axiom.java	2009-03-24 18:42:49 UTC (rev 1626)
@@ -104,5 +104,15 @@
   public RDFNode getObject() throws URIParseException {
     return predicate.getObject();
   }
+
+  @Override
+  public CanonicalStatement getCanonical() {
+    return new CanonicalStatement(predicate.getCanonical());
+  }
+
+  /** @see java.lang.Object#toString() */
+  public String toString() {
+    return predicate.toString() + ".";
+  }
 }
 

Modified: trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/BPredicate.java
===================================================================
--- trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/BPredicate.java	2009-03-24 01:12:37 UTC (rev 1625)
+++ trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/BPredicate.java	2009-03-24 18:42:49 UTC (rev 1626)
@@ -107,7 +107,7 @@
 
   //inheritdoc
   public String toString() {
-    return "BPredicate: " + label + "(" + left + "," + right + ")";
+    return label.toString() + "(" + left + "," + right + ")";
   }
 
   //inheritdoc
@@ -132,5 +132,14 @@
       graphAnnotation = MulgaraGraphs.getPredicateGraph(((URIReference)predicate).getURI());
     }
   }
+
+  /**
+   * Creates a canonical form for this predicate.
+   * @see org.mulgara.krule.rlog.ast.Predicate#getCanonical()
+   */
+  @Override
+  CanonicalPredicate getCanonical() {
+    return new CanonicalPredicate(left, (PredicateParam)label, right);
+  }
 }
 

Copied: trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/CanonicalPredicate.java (from rev 1625, branches/consistency/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/CanonicalPredicate.java)
===================================================================
--- trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/CanonicalPredicate.java	                        (rev 0)
+++ trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/CanonicalPredicate.java	2009-03-24 18:42:49 UTC (rev 1626)
@@ -0,0 +1,197 @@
+/*
+ * 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.krule.rlog.ast;
+
+/**
+ * Represents a canonicalization of a predicate.
+ *
+ * @created Mar 4, 2009
+ * @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 CanonicalPredicate implements Comparable<CanonicalPredicate> {
+
+  /** The number of elements for a binary predicate. */
+  private static final int BINARY_LENGTH = 3;
+
+  /** The number of elements for a unary predicate. */
+  private static final int UNARY_LENGTH = 2;
+
+  /** The number of elements for a null predicate. */
+  private static final int NULL_LENGTH = 0;
+
+  /** The different in ID between inverted predicates and the original predicate. */
+  private static final int INVERT_DIFF = 8;
+
+  /** The elements of the predicate */
+  private PredicateParam[] elements;
+
+  /** A flag to indicate that this predicate is inverted. */
+  private boolean invertFlag = false;
+
+  /** An ID used internally for comparisons between differing types. */
+  private int typeId;
+
+  /**
+   * Create a new canonicalized form for a null predicate.
+   */
+  CanonicalPredicate() {
+    elements = new PredicateParam[NULL_LENGTH];
+    typeId = elements.length;
+  }
+
+
+  /**
+   * Create a new canonicalized form for a binary predicate.
+   * @param s The subject of the predicate.
+   * @param p The value of the predicate.
+   * @param o The object of the predicate.
+   */
+  CanonicalPredicate(PredicateParam s, PredicateParam p, PredicateParam o) {
+    elements = new PredicateParam[BINARY_LENGTH];
+    elements[0] = s;
+    elements[1] = p;
+    elements[2] = o;
+    typeId = elements.length;
+  }
+
+
+  /**
+   * Create a new canonicalized form for a unary predicate.
+   * @param t The type of the predicate. 
+   * @param v The value of the predicate.
+   */
+  CanonicalPredicate(PredicateParam t, PredicateParam v) {
+    elements = new PredicateParam[UNARY_LENGTH];
+    elements[0] = t;
+    elements[1] = v;
+    typeId = elements.length;
+  }
+
+
+  /**
+   * Changes this predicate to an inverted one.
+   */
+  public CanonicalPredicate invert() {
+    invertFlag = !invertFlag;
+    typeId += invertFlag ? INVERT_DIFF : -INVERT_DIFF;
+    return this;
+  }
+
+
+  /**
+   * Changes this predicate to an inverted one.
+   */
+  public boolean isInverted() {
+    return invertFlag;
+  }
+
+
+  /**
+   * Updates variables to a canonical form
+   * @param con The object with the update state for the variables.
+   */
+  public void renameVariables(VariableCanonicalizer con) {
+    for (int i = 0; i < elements.length; i++) {
+      if (elements[i] instanceof Variable) {
+        elements[i] = con.get((Variable)elements[i]);
+      }
+    }
+  }
+
+
+  /**
+   * @see java.lang.Object#toString()
+   */
+  public String toString() {
+    StringBuilder s = new StringBuilder();
+    if (invertFlag) s.append("~");
+    switch (elements.length) {
+    case NULL_LENGTH:
+      s.append("<<null>>");
+      break;
+    case UNARY_LENGTH:
+      s.append(elements[0]);
+      s.append("(").append(elements[1]).append(")");
+      break;
+    case BINARY_LENGTH:
+      s.append(elements[1]);
+      s.append("(").append(elements[0]).append(", ");
+      s.append(elements[2]).append(")");
+      break;
+    default:
+      throw new IllegalStateException("Illegal predicate structure. Length = " + elements.length);
+    }
+    return s.toString();
+  }
+
+
+  /**
+   * Tests if this predicate equals another.
+   * @see java.lang.Object#equals(java.lang.Object)
+   */
+  public boolean equals(Object o) {
+    if (!(o instanceof CanonicalPredicate)) return false;
+    CanonicalPredicate cp = (CanonicalPredicate)o;
+    if (elements.length != cp.elements.length) return false;
+    for (int i = 0; i < elements.length; i++) {
+      if (!elements[i].equals(cp.elements[i])) return false;
+    }
+    return true;
+  }
+
+
+  /**
+   * Generate a repeatable hashcode for this predicate.
+   * @see java.lang.Object#hashCode()
+   */
+  public int hashCode() {
+    final int[] seed = new int[] { 7, 13, 17, 19 };
+    int result = 5;
+    for (int i = 0; i < elements.length; i++) result += seed[i] * elements[i].hashCode();
+    return result;
+  }
+
+
+  /**
+   * Compare this predicate to another.
+   * @see java.lang.Comparable#compareTo(java.lang.Object)
+   */
+  public int compareTo(CanonicalPredicate cpred) {
+    if (typeId != cpred.typeId) return typeId - cpred.typeId;
+    return compareOnElt(0, cpred);
+  }
+
+
+  /**
+   * Compare this predicate, first on equivalent types, and then on type.
+   * Order by PredicateLiteral, StringLiteral, IntegerLiteral, Var.
+   * @param i The element being compared at this stage.
+   * @param cpred The other CanonicalPredicate to compare against.
+   * @return &gt;0 if this object occurs after cpred, &lt;0 if this object is before cpred,
+   *         and 0 if this object is equal to cpred.
+   */
+  private int compareOnElt(int i, CanonicalPredicate cpred) {
+    assert i >= 0 && i < elements.length;
+    int typeDiff = elements[i].orderId() - cpred.elements[i].orderId();
+    // if the types are the same then compare
+    if (typeDiff == 0) {
+      int r = elements[i].compareTo(cpred.elements[i]);
+      // if the elements are equal, then move to the next element
+      return r == 0 && ++i < elements.length ? compareOnElt(i, cpred) : r;
+    }
+    return typeDiff;
+  }
+
+}

Copied: trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/CanonicalPredicateTest.java (from rev 1625, branches/consistency/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/CanonicalPredicateTest.java)
===================================================================
--- trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/CanonicalPredicateTest.java	                        (rev 0)
+++ trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/CanonicalPredicateTest.java	2009-03-24 18:42:49 UTC (rev 1626)
@@ -0,0 +1,179 @@
+/*
+ * 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.krule.rlog.ast;
+
+import org.apache.log4j.Logger;
+import org.mulgara.krule.rlog.ParseContext;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+/**
+ * 
+ *
+ * @created Mar 10, 2009
+ * @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 CanonicalPredicateTest extends TestCase {
+
+  @SuppressWarnings("unused")
+  private static final Logger logger = Logger.getLogger(CanonicalPredicateTest.class.getName());
+
+  ParseContext context = new ParseContext();
+
+  /**
+   * Create the test
+   * @param name The name of the test.
+   */
+  public CanonicalPredicateTest(String name) {
+    super(name);
+  }
+
+
+  /**
+   * Hook from which the test runner can obtain a test suite.
+   *
+   * @return the test suite
+   */
+  public static Test suite() {
+    TestSuite suite = new TestSuite(CanonicalPredicateTest.class);
+    return suite;
+  }
+
+
+  /**
+   * This is a hook for initializing a test.
+   */
+  public void setup() {
+  }
+
+
+  /**
+   * Test that a predicate can be canonicalized.
+   *
+   * @throws Exception Due to any kind of internal failure.
+   */
+  public void testBinaryLiteral() throws Exception {
+    PredicateParam left = new BPredicateLiteral("foo", "bar", context);
+    PredicateParam right = new BPredicateLiteral("foo", "baz", context);
+    BPredicateLabel label = new BPredicateLiteral("foo", "pred", context);
+    Predicate p = new BPredicate(label, left, right, context);
+    CanonicalPredicate cp = p.getCanonical();
+    assertEquals("foo:pred(foo:bar, foo:baz)", cp.toString());
+    cp.invert();
+    assertEquals("~foo:pred(foo:bar, foo:baz)", cp.toString());
+  }
+
+  /**
+   * Test that a type predicate can be canonicalized.
+   *
+   * @throws Exception Due to any kind of internal failure.
+   */
+  public void testTypeLiteral() throws Exception {
+    PredicateParam left = new BPredicateLiteral("foo", "bar", context);
+    TypeLabel label = new TypeLiteral("foo", "Type", context);
+    Predicate p = new TypeStatement(label, left, context);
+    CanonicalPredicate cp = p.getCanonical();
+    assertEquals("foo:Type(foo:bar)", cp.toString());
+    cp.invert();
+    assertEquals("~foo:Type(foo:bar)", cp.toString());
+  }
+
+  /**
+   * Test that a binary predicate with variables can be canonicalized.
+   *
+   * @throws Exception Due to any kind of internal failure.
+   */
+  public void testBinaryVar() throws Exception {
+    PredicateParam left = new BPredicateLiteral("foo", "bar", context);
+    PredicateParam right = new Variable("X");
+    BPredicateLabel label = new BPredicateLiteral("foo", "pred", context);
+    Predicate p = new BPredicate(label, left, right, context);
+    CanonicalPredicate cp = p.getCanonical();
+    assertEquals("foo:pred(foo:bar, ?X)", cp.toString());
+    VariableCanonicalizer con = new VariableCanonicalizer();
+    cp.renameVariables(con);
+    assertEquals("foo:pred(foo:bar, ?V1)", cp.toString());
+
+    left = new Variable("A");
+    right = new Variable("B");
+    label = new Variable("C");
+    p = new BPredicate(label, left, right, context);
+    cp = p.getCanonical();
+    assertEquals("?C(?A, ?B)", cp.toString());
+    con = new VariableCanonicalizer();
+    cp.renameVariables(con);
+    assertEquals("?V2(?V1, ?V3)", cp.toString());
+    cp.renameVariables(con);
+    assertEquals("?V5(?V4, ?V6)", cp.toString());
+
+    cp = p.getCanonical();
+    cp.renameVariables(con);
+    assertEquals("?V2(?V1, ?V3)", cp.toString());
+  }
+
+  /**
+   * Test that a variable type predicate can be canonicalized.
+   *
+   * @throws Exception Due to any kind of internal failure.
+   */
+  public void testTypeVariable() throws Exception {
+    PredicateParam left = new BPredicateLiteral("foo", "bar", context);
+    TypeLabel label = new Variable("X");
+    Predicate p = new TypeStatement(label, left, context);
+    CanonicalPredicate cp = p.getCanonical();
+    assertEquals("?X(foo:bar)", cp.toString());
+    VariableCanonicalizer con = new VariableCanonicalizer();
+    cp.renameVariables(con);
+    assertEquals("?V1(foo:bar)", cp.toString());
+
+    left = new Variable("B");
+    label = new Variable("A");
+    p = new TypeStatement(label, left, context);
+    cp = p.getCanonical();
+    assertEquals("?A(?B)", cp.toString());
+    con = new VariableCanonicalizer();
+    cp.renameVariables(con);
+    assertEquals("?V1(?V2)", cp.toString());
+
+    cp.renameVariables(con);
+    assertEquals("?V3(?V4)", cp.toString());
+
+    cp = p.getCanonical();
+    cp.renameVariables(con);
+    assertEquals("?V1(?V2)", cp.toString());
+  }
+
+  /**
+   * Test that a variable type predicate can be equal.
+   *
+   * @throws Exception Due to any kind of internal failure.
+   */
+  public void testTypeEqual() throws Exception {
+    PredicateParam left = new BPredicateLiteral("foo", "bar", context);
+    TypeLabel label = new Variable("X");
+    Predicate p = new TypeStatement(label, left, context);
+    CanonicalPredicate cp = p.getCanonical();
+    VariableCanonicalizer con = new VariableCanonicalizer();
+    cp.renameVariables(con);
+    assertEquals("?V1(foo:bar)", cp.toString());
+
+    Predicate p2 = new TypeStatement(new Variable("V1"), left, context);
+    assertNotSame(p, p2);
+    assertEquals(cp, p2.getCanonical());
+  }
+
+}

Copied: trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/CanonicalStatement.java (from rev 1625, branches/consistency/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/CanonicalStatement.java)
===================================================================
--- trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/CanonicalStatement.java	                        (rev 0)
+++ trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/CanonicalStatement.java	2009-03-24 18:42:49 UTC (rev 1626)
@@ -0,0 +1,111 @@
+/*
+ * 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.krule.rlog.ast;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Represents a canonicalized form of a statement. Used for comparing statements
+ * to check them for redundancy.
+ *
+ * @created Mar 12, 2009
+ * @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 CanonicalStatement {
+
+  /** A sorted list of predicates */
+  private List<CanonicalPredicate> body;
+
+  /** The head of the rule. May be empty. */
+  private CanonicalPredicate head;
+
+
+  /**
+   * Creates a canonical statement to represent an axiom.
+   * @param h The predicate in the axiom.
+   */
+  CanonicalStatement(CanonicalPredicate h) {
+    this(h, null);
+  }
+
+
+  /**
+   * Creates a canonical statement to represent a rule.
+   * @param h The predicate in the head of the rule.
+   * @param b The <em>sorted</em> predicates in the body of the rule.
+   */
+  CanonicalStatement(CanonicalPredicate h, List<CanonicalPredicate> b) {
+    if (b == null) body = Collections.emptyList();
+    else body = b;
+    head = h;
+    renameVariables();
+  }
+
+
+  /**
+   * Test is this statement is the same as another.
+   * @see java.lang.Object#equals(java.lang.Object)
+   */
+  public boolean equals(Object o) {
+    if (!(o instanceof CanonicalStatement)) return false;
+    CanonicalStatement s = (CanonicalStatement)o;
+    return equal(head, s.head) && body.equals(s.body);
+  }
+
+
+  /**
+   * Generates a hashcode which merges the hashcodes of the body and head.
+   * @see java.lang.Object#hashCode()
+   */
+  public int hashCode() {
+    int hhc = head.hashCode();
+    return (hhc >>> 16 | hhc << 16) ^ body.hashCode();
+  }
+
+
+  public String toString() {
+    StringBuilder s = new StringBuilder();
+    s.append(head);
+    s.append(" :- ");
+    for (int p = 0; p < body.size(); p++) {
+      if (p == 0) s.append(", ");
+      s.append(body.get(p));
+    }
+    s.append(".");
+    return s.toString();
+  }
+
+  /**
+   * Renames the variables into a canonical form.
+   */
+  private void renameVariables() {
+    VariableCanonicalizer vc = new VariableCanonicalizer();
+    for (CanonicalPredicate p: body) p.renameVariables(vc);
+    head.renameVariables(vc);
+  }
+
+
+  /**
+   * Performs an equality comparison for values that may be null.
+   * @param a The first object to be compared. May be null.
+   * @param b The second object to be compared. May be null.
+   * @return <code>true</code> if both a and b are null, or if they are equal.
+   *         <code>false</code> otherwise.
+   */
+  private static final boolean equal(Object a, Object b) {
+    return a == null ? b == null : a.equals(b);
+  }
+}

Copied: trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/CheckRule.java (from rev 1625, branches/consistency/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/CheckRule.java)
===================================================================
--- trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/CheckRule.java	                        (rev 0)
+++ trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/CheckRule.java	2009-03-24 18:42:49 UTC (rev 1626)
@@ -0,0 +1,49 @@
+/*
+ * 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.krule.rlog.ast;
+
+import java.util.List;
+
+import org.mulgara.krule.rlog.ParseContext;
+
+/**
+ * A headless rule, used for checking validity.
+ *
+ * @created Mar 3, 2009
+ * @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 CheckRule extends Rule {
+
+  /**
+   * @param body The body of the rule.
+   * @param context The parsing context for this rule.
+   */
+  public CheckRule(List<Predicate> body, ParseContext context) {
+    super(body, context);
+  }
+
+
+  /** @see java.lang.Object#toString() */
+  public String toString() {
+    StringBuilder sb = new StringBuilder(":- ");
+    for (int b = 0; b < body.size(); b++) {
+      if (b != 0) sb.append(", ");
+      sb.append(body.get(b));
+    }
+    sb.append(".");
+    return sb.toString();
+  }
+
+}

Modified: trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/IntegerLiteral.java
===================================================================
--- trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/IntegerLiteral.java	2009-03-24 01:12:37 UTC (rev 1625)
+++ trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/IntegerLiteral.java	2009-03-24 18:42:49 UTC (rev 1626)
@@ -73,5 +73,26 @@
   public RDFNode getRDFNode() {
     return new Literal(value.toString(), RDF.XSD_LONG);
   }
+
+
+  /**
+   * Order by PredicateLiteral, StringLiteral, IntegerLiteral, Var.
+   * @see java.lang.Comparable#compareTo(java.lang.Object)
+   */
+  public int compareTo(PredicateParam o) {
+    if (o instanceof IntegerLiteral) return value.compareTo(((IntegerLiteral)o).value);
+    // Smaller than Variable, larger than everything else
+    return (o instanceof Variable) ? -1 : 1;
+  }
+
+
+  /**
+   * Defines the ordering that this class occurs in, compared to other PredicateParams
+   * @see org.mulgara.krule.rlog.ast.PredicateParam#orderId()
+   */
+  public int orderId() {
+    return INTEGER_LITERAL_ID;
+  }
+
 }
 

Modified: trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/InvertedPredicate.java
===================================================================
--- trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/InvertedPredicate.java	2009-03-24 01:12:37 UTC (rev 1625)
+++ trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/InvertedPredicate.java	2009-03-24 18:42:49 UTC (rev 1626)
@@ -91,5 +91,10 @@
     return invertPredicate.getObject();
   }
 
+  @Override
+  CanonicalPredicate getCanonical() {
+    return invertPredicate.getCanonical().invert();
+  }
+
 }
 

Modified: trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/NullPredicate.java
===================================================================
--- trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/NullPredicate.java	2009-03-24 01:12:37 UTC (rev 1625)
+++ trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/NullPredicate.java	2009-03-24 18:42:49 UTC (rev 1626)
@@ -90,5 +90,15 @@
     throw new UnsupportedOperationException();
   }
 
+  /** @see java.lang.Object#toString() */
+  public String toString() {
+    return "";
+  }
+
+  @Override
+  CanonicalPredicate getCanonical() {
+    return new CanonicalPredicate();
+  }
+
 }
 

Modified: trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/Predicate.java
===================================================================
--- trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/Predicate.java	2009-03-24 01:12:37 UTC (rev 1625)
+++ trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/Predicate.java	2009-03-24 18:42:49 UTC (rev 1626)
@@ -172,6 +172,12 @@
   }
 
   /**
+   * Returns a canonical version of this predicate.
+   * @return A new canonical predicate, with canonicalized elements.
+   */
+  abstract CanonicalPredicate getCanonical();
+
+  /**
    * Converts a Variable to a Var. Accepts multiple types.
    * @param v The Variable to convert. This must be a Variable.
    * @return a new Var that is equivalent to the v.

Modified: trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/PredicateLiteral.java
===================================================================
--- trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/PredicateLiteral.java	2009-03-24 01:12:37 UTC (rev 1625)
+++ trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/PredicateLiteral.java	2009-03-24 18:42:49 UTC (rev 1626)
@@ -79,5 +79,28 @@
     if (ref == null) throw new URIParseException(name);
     return ref;
   }
+
+  /**
+   * Order by PredicateLiteral, StringLiteral, IntegerLiteral, Var.
+   * @see java.lang.Comparable#compareTo(java.lang.Object)
+   */
+  public int compareTo(PredicateParam o) {
+    // smaller than everything except other PredicateLiterals
+    return (o instanceof PredicateLiteral) ? name.compareTo(((PredicateLiteral)o).name) : -1;
+  }
+
+  /**
+   * Defines the ordering that this class occurs in, compared to other PredicateParams
+   * @see org.mulgara.krule.rlog.ast.PredicateParam#orderId()
+   */
+  public int orderId() {
+    return PREDICATE_LITERAL_ID;
+  }
+
+  /** @see java.lang.Object#toString() */
+  public String toString() {
+    return name;
+  }
+
 }
 

Modified: trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/PredicateParam.java
===================================================================
--- trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/PredicateParam.java	2009-03-24 01:12:37 UTC (rev 1625)
+++ trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/PredicateParam.java	2009-03-24 18:42:49 UTC (rev 1626)
@@ -26,9 +26,17 @@
  * @copyright &copy; 2007 <a href="mailto:pgearon at users.sourceforge.net">Topaz Project</a>
  * @licence <a href="http://www.opensource.org/licenses/apache2.0.php">Apache License, Version 2.0</a>
  */
-public interface PredicateParam {
+public interface PredicateParam extends Comparable<PredicateParam> {
   public void accept(TreeWalker walker);
   public void print(int indent);
   public RDFNode getRDFNode() throws URIParseException;
+  public int orderId();
+
+  // Identifiers for ordering subtypes
+  static final int PREDICATE_LITERAL_ID = 1;
+  static final int STRING_LITERAL_ID = 2;
+  static final int INTEGER_LITERAL_ID = 3;
+  static final int VARIABLE_ID = 4;
+
 }
 

Modified: trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/Rule.java
===================================================================
--- trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/Rule.java	2009-03-24 01:12:37 UTC (rev 1625)
+++ trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/Rule.java	2009-03-24 18:42:49 UTC (rev 1626)
@@ -16,6 +16,7 @@
 
 package org.mulgara.krule.rlog.ast;
 
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashSet;
@@ -31,6 +32,7 @@
 import org.mulgara.krule.rlog.rdf.RDFNode;
 import org.mulgara.krule.rlog.rdf.URIReference;
 import org.mulgara.krule.rlog.rdf.Var;
+import org.mulgara.util.functional.C;
 
 /**
  * Represents a rule statement.
@@ -56,7 +58,7 @@
   /** The name of this rule. Used in RDF. */
   private String name;
 
-  public Rule(List<Predicate> body,  ParseContext context) {
+  protected Rule(List<Predicate> body,  ParseContext context) {
     this(NullPredicate.NULL, body, context);
   }
 
@@ -209,6 +211,28 @@
     return triggers;
   }
 
+  @Override
+  public CanonicalStatement getCanonical() {
+    List<CanonicalPredicate> list = new ArrayList<CanonicalPredicate>(body.size() + 1);
+    // reorder the predicates
+    for (Predicate p: body) C.ascendingInsert(list, p.getCanonical());
+    return new CanonicalStatement(head.getCanonical(), list);
+  }
+
+
+  /** @see java.lang.Object#toString() */
+  public String toString() {
+    StringBuilder sb = new StringBuilder(head.toString());
+    sb.append(" :- ");
+    for (int b = 0; b < body.size(); b++) {
+      if (b != 0) sb.append(", ");
+      sb.append(body.get(b));
+    }
+    sb.append(".");
+    return sb.toString();
+  }
+
+
   /**
    * Checks that all variables in the head are found in the body, and that every subtracted predicate
    * contains at least one variable that appears in the standard matching predicates.

Modified: trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/Statement.java
===================================================================
--- trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/Statement.java	2009-03-24 01:12:37 UTC (rev 1625)
+++ trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/Statement.java	2009-03-24 18:42:49 UTC (rev 1626)
@@ -35,5 +35,11 @@
     super(context);
   }
 
+  /**
+   * Get a list of canonicalized predicates to represent the statement. The body of the
+   * statement must be sorted, but if a head exists then it must be at the end.
+   * @return A list containing the statement in canonical form.
+   */
+  public abstract CanonicalStatement getCanonical();
 }
 

Modified: trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/StringLiteral.java
===================================================================
--- trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/StringLiteral.java	2009-03-24 01:12:37 UTC (rev 1625)
+++ trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/StringLiteral.java	2009-03-24 18:42:49 UTC (rev 1626)
@@ -67,5 +67,24 @@
   public RDFNode getRDFNode() {
     return new Literal(value.toString());
   }
+
+  /**
+   * Order by PredicateLiteral, StringLiteral, IntegerLiteral, Var.
+   * @see java.lang.Comparable#compareTo(java.lang.Object)
+   */
+  public int compareTo(PredicateParam o) {
+    if (o instanceof StringLiteral) return value.compareTo(((StringLiteral)o).value);
+    // larger than PredicateLiteral, smaller than everything else
+    return (o instanceof PredicateLiteral) ? 1 : -1;
+  }
+
+  /**
+   * Defines the ordering that this class occurs in, compared to other PredicateParams
+   * @see org.mulgara.krule.rlog.ast.PredicateParam#orderId()
+   */
+  public int orderId() {
+    return STRING_LITERAL_ID;
+  }
+
 }
 

Modified: trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/TypeStatement.java
===================================================================
--- trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/TypeStatement.java	2009-03-24 01:12:37 UTC (rev 1625)
+++ trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/TypeStatement.java	2009-03-24 18:42:49 UTC (rev 1626)
@@ -97,7 +97,7 @@
 
   // inheritdoc
   public String toString() {
-    return "TypeStatement: type(" + typeLabel + ") param(" + param + ")";
+    return typeLabel.toString() + "(" + param + ")";
   }
 
   //inheritdoc
@@ -113,7 +113,18 @@
     return typeLabel.hashCode() * 37 + param.hashCode();
   }
 
+
   /**
+   * Creates a canonical form for this predicate.
+   * @see org.mulgara.krule.rlog.ast.Predicate#getCanonical()
+   */
+  @Override
+  CanonicalPredicate getCanonical() {
+    return new CanonicalPredicate((PredicateParam)typeLabel, param);
+  }
+
+
+  /**
    * Search for the type in the special graphs, and annotate this predicate
    * to any detected graphs.
    */

Modified: trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/Variable.java
===================================================================
--- trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/Variable.java	2009-03-24 01:12:37 UTC (rev 1625)
+++ trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/Variable.java	2009-03-24 18:42:49 UTC (rev 1626)
@@ -47,7 +47,7 @@
   }
 
   public String toString() {
-    return "var(" + name + ")";
+    return "?" + name;
   }
 
   /** {@inheritDoc} */
@@ -66,5 +66,24 @@
     return name.hashCode();
   }
 
+
+  /**
+   * Order by PredicateLiteral, StringLiteral, IntegerLiteral, Var.
+   * @see java.lang.Comparable#compareTo(java.lang.Object)
+   */
+  public int compareTo(PredicateParam o) {
+    // larger than everything except other variables
+    return (o instanceof Variable) ? name.compareTo(((Variable)o).name) : 1;
+  }
+
+
+  /**
+   * Defines the ordering that this class occurs in, compared to other PredicateParams
+   * @see org.mulgara.krule.rlog.ast.PredicateParam#orderId()
+   */
+  public int orderId() {
+    return VARIABLE_ID;
+  }
+
 }
 

Copied: trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/VariableCanonicalizer.java (from rev 1625, branches/consistency/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/VariableCanonicalizer.java)
===================================================================
--- trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/VariableCanonicalizer.java	                        (rev 0)
+++ trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/VariableCanonicalizer.java	2009-03-24 18:42:49 UTC (rev 1626)
@@ -0,0 +1,47 @@
+/*
+ * 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.krule.rlog.ast;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Maintains state for updating variables to a canonical form.
+ *
+ * @created Mar 4, 2009
+ * @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 VariableCanonicalizer {
+
+  /** Maintains the state of the variables that have already been mapped to new names. */
+  private Map<Variable,Variable> varMap = new HashMap<Variable,Variable>();
+
+  /** The index used to name variables. */
+  private int varIndex = 1;
+
+  /**
+   * Get the canonical variable that a variable is supposed to map to.
+   * @param old The old variable to be replaced.
+   * @return The new variable that the old variable is mapped to.
+   */
+  public Variable get(Variable old) {
+    Variable newVar = varMap.get(old);
+    if (newVar == null) {
+      newVar = new Variable("V" + varIndex++);
+      varMap.put(old, newVar);
+    }
+    return newVar;
+  }
+}

Modified: trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/output/AxiomGenerator.java
===================================================================
--- trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/output/AxiomGenerator.java	2009-03-24 01:12:37 UTC (rev 1625)
+++ trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/output/AxiomGenerator.java	2009-03-24 18:42:49 UTC (rev 1626)
@@ -31,7 +31,7 @@
 import java.util.Collection;
 import java.util.List;
 
-import static org.mulgara.krule.KruleLoader.*;
+import static org.mulgara.query.rdf.Krule.*;
 
 /**
  * Writes variables to a list of triples.

Modified: trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/output/RuleGenerator.java
===================================================================
--- trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/output/RuleGenerator.java	2009-03-24 01:12:37 UTC (rev 1625)
+++ trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/output/RuleGenerator.java	2009-03-24 18:42:49 UTC (rev 1626)
@@ -18,6 +18,7 @@
 
 import org.jrdf.vocabulary.RDF;
 import org.mulgara.krule.rlog.ParseException;
+import org.mulgara.krule.rlog.ast.CheckRule;
 import org.mulgara.krule.rlog.ast.Predicate;
 import org.mulgara.krule.rlog.ast.Rule;
 import org.mulgara.krule.rlog.parser.URIParseException;
@@ -34,24 +35,28 @@
 import java.net.URISyntaxException;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.List;
 
-import static org.mulgara.krule.KruleLoader.ARGUMENT;
-import static org.mulgara.krule.KruleLoader.CONSTRAINT_CONJUNCTION;
-import static org.mulgara.krule.KruleLoader.DIFFERENCE;
-import static org.mulgara.krule.KruleLoader.HAS_QUERY;
-import static org.mulgara.krule.KruleLoader.HAS_WHERE_CLAUSE;
-import static org.mulgara.krule.KruleLoader.HAS_SUBJECT;
-import static org.mulgara.krule.KruleLoader.HAS_PREDICATE;
-import static org.mulgara.krule.KruleLoader.HAS_OBJECT;
-import static org.mulgara.krule.KruleLoader.HAS_GRAPH;
-import static org.mulgara.krule.KruleLoader.NAME;
-import static org.mulgara.krule.KruleLoader.QUERY;
-import static org.mulgara.krule.KruleLoader.RULE;
-import static org.mulgara.krule.KruleLoader.SELECTION_VARS;
-import static org.mulgara.krule.KruleLoader.SIMPLE_CONSTRAINT;
-import static org.mulgara.krule.KruleLoader.TRIGGERS;
-import static org.mulgara.krule.KruleLoader.VARIABLE;
+import static org.mulgara.query.rdf.Krule.ARGUMENT;
+import static org.mulgara.query.rdf.Krule.CHECK;
+import static org.mulgara.query.rdf.Krule.CONSTRAINT_CONJUNCTION;
+import static org.mulgara.query.rdf.Krule.DIFFERENCE;
+import static org.mulgara.query.rdf.Krule.HAS_QUERY;
+import static org.mulgara.query.rdf.Krule.HAS_WHERE_CLAUSE;
+import static org.mulgara.query.rdf.Krule.HAS_SUBJECT;
+import static org.mulgara.query.rdf.Krule.HAS_PREDICATE;
+import static org.mulgara.query.rdf.Krule.HAS_OBJECT;
+import static org.mulgara.query.rdf.Krule.HAS_GRAPH;
+import static org.mulgara.query.rdf.Krule.MINUEND;
+import static org.mulgara.query.rdf.Krule.NAME;
+import static org.mulgara.query.rdf.Krule.QUERY;
+import static org.mulgara.query.rdf.Krule.RULE;
+import static org.mulgara.query.rdf.Krule.SELECTION_VARS;
+import static org.mulgara.query.rdf.Krule.SIMPLE_CONSTRAINT;
+import static org.mulgara.query.rdf.Krule.SUBTRAHEND;
+import static org.mulgara.query.rdf.Krule.TRIGGERS;
+import static org.mulgara.query.rdf.Krule.VARIABLE;
 
 /**
  * Writes rules to a list of triples.
@@ -71,6 +76,9 @@
   /** The node for the krule:Rule type */
   private final long kruleRule;
 
+  /** The node for the krule:Check type */
+  private final long kruleCheck;
+
   /** The node for the krule:Query type */
   private final long kruleQuery;
 
@@ -113,6 +121,12 @@
   /** The node for the krule:argument predicate */
   private final long kruleArgument;
 
+  /** The node for the krule:minuend predicate */
+  private final long kruleMinuend;
+
+  /** The node for the krule:subtrahend predicate */
+  private final long kruleSubtrahend;
+
   /** The node for the krule:name predicate */
   private final long kruleName;
 
@@ -129,6 +143,7 @@
     this.rules = rules;
     rdfSeq = resolverSession.localize(new URIReferenceImpl(RDF.SEQ));
     kruleRule = resolverSession.localize(RULE);
+    kruleCheck = resolverSession.localize(CHECK);
     kruleQuery = resolverSession.localize(QUERY);
     kruleDifference = resolverSession.localize(DIFFERENCE);
     kruleConjunction = resolverSession.localize(CONSTRAINT_CONJUNCTION);
@@ -143,6 +158,8 @@
     kruleSelVars = resolverSession.localize(SELECTION_VARS);
     kruleHasWhereClause = resolverSession.localize(HAS_WHERE_CLAUSE);
     kruleArgument = resolverSession.localize(ARGUMENT);
+    kruleMinuend = resolverSession.localize(MINUEND);
+    kruleSubtrahend = resolverSession.localize(SUBTRAHEND);
     kruleName = resolverSession.localize(NAME);
     initSeqTo(3);
   }
@@ -180,18 +197,24 @@
   private List<long[]> emitRule(List<long[]> triples, Rule rule) throws NodePoolException, ParseException, LocalizeException, URISyntaxException, URIParseException {
     long ruleNode = toKruleNode(rule.getName());
 
-    // rule rdf:type kruleRule
-    add(triples, ruleNode, rdfType, kruleRule);
+    if (rule instanceof CheckRule) {
+      // rule rdf:type kruleCheck
+      add(triples, ruleNode, rdfType, (rule instanceof CheckRule) ? kruleCheck : kruleRule);
+    } else {
+      // rule rdf:type kruleRule
+      add(triples, ruleNode, rdfType, kruleRule);
+      emitTriggers(triples, ruleNode, rule.getTriggers());
+    }
 
-    emitTriggers(triples, ruleNode, rule.getTriggers());
-
     // query rdf:type krule:Query
     // rule krule:hasQuery query
     long query = newBlankNode();
     add(triples, query, rdfType, kruleQuery);
     add(triples, ruleNode, kruleHasQuery, query);
 
-    emitSelection(triples, query, rule.getHead());
+    if (rule instanceof CheckRule) emitSelection(triples, query, rule.getVariables()); 
+    else emitSelection(triples, query, rule.getHead());
+
     emitWhereClause(triples, query, rule.getBody(), rule.getBodySubtractions());
 
     return triples;
@@ -218,18 +241,47 @@
    * @throws URISyntaxException If a selected URI is incorrectly formed.
    */
   private void emitSelection(List<long[]> triples, long query, Predicate selection) throws URIParseException, LocalizeException, URISyntaxException {
+    emitSelection(triples, query, Collections.singletonList(selection));
+  }
+
+
+  /**
+   * Adds the head of a rule to the triples.
+   * @param triples The list of triples to append to.
+   * @param selection The list of predicates that makes up the head of a rule.
+   * @throws LocalizeException Unable to create a new blank node.
+   * @throws URISyntaxException If a selected URI is incorrectly formed.
+   */
+  private void emitSelection(List<long[]> triples, long query, List<Predicate> selection) throws URIParseException, LocalizeException, URISyntaxException {
+    List<RDFNode> sel = new ArrayList<RDFNode>();
+    for (Predicate p: selection) {
+      sel.add(p.getSubject());
+      sel.add(p.getPredicate());
+      sel.add(p.getObject());
+    }
+    emitSelection(triples, query, sel);
+  }
+
+
+  /**
+   * Adds the selection elements of a rule to the triples.
+   * @param triples The list of triples to append to.
+   * @param selection The selection that makes up the variables or the head of a rule.
+   * @throws LocalizeException Unable to create a new blank node.
+   * @throws URISyntaxException If a selected URI is incorrectly formed.
+   */
+  private void emitSelection(List<long[]> triples, long query, Collection<? extends RDFNode> selection) throws URIParseException, LocalizeException, URISyntaxException {
     // seq rdf:type rdf:Seq
     // query krule:selectionVariables seq
     long seq = newBlankNode();
     add(triples, seq, rdfType, rdfSeq);
     add(triples, query, kruleSelVars, seq);
 
-    // seq rdf:_1 getSubject()
-    // seq rdf:_2 getPredicate()
-    // seq rdf:_3 getObject()
-    add(triples, seq, getSeq(1), toKruleNode(selection.getSubject().getRdfLabel()));
-    add(triples, seq, getSeq(2), toKruleNode(selection.getPredicate().getRdfLabel()));
-    add(triples, seq, getSeq(3), toKruleNode(selection.getObject().getRdfLabel()));
+    // seq rdf:_n selection(n) ...
+    int n = 1;
+    for (RDFNode s: selection) {
+      add(triples, seq, getSeq(n++), toKruleNode(s.getRdfLabel()));
+    }
   }
 
 
@@ -262,21 +314,21 @@
    * @throws URISyntaxException If one of the URIs in the constraint has an invalid syntax.
    */
   private void emitSubtractions(List<long[]> triples, long diff, List<Predicate> body, List<Predicate> subs) throws URIParseException, LocalizeException, URISyntaxException {
-    // constraintExpr rdf:type krule:Difference
+    // diff rdf:type krule:Difference
     add(triples, diff, rdfType, kruleDifference);
 
     long argument = newBlankNode();
-    // diff krule:argument argument
-    add(triples, diff, kruleArgument, argument);
+    // diff krule:minuend argument
+    add(triples, diff, kruleMinuend, argument);
 
     int lastElt = subs.size() - 1;
     if (lastElt == 0) emitConjunction(triples, argument, body);
     else emitSubtractions(triples, argument, body, subs.subList(0, lastElt));
 
     // last argument in subtraction
-    // diff krule:argument argument
+    // diff krule:subtrahend argument
     argument = newBlankNode();
-    add(triples, diff, kruleArgument, argument);
+    add(triples, diff, kruleSubtrahend, argument);
 
     emitSimpleConstraint(triples, argument, subs.get(lastElt));
   }

Modified: trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/output/RuleWriter.java
===================================================================
--- trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/output/RuleWriter.java	2009-03-24 01:12:37 UTC (rev 1625)
+++ trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/output/RuleWriter.java	2009-03-24 18:42:49 UTC (rev 1626)
@@ -16,12 +16,16 @@
 
 package org.mulgara.krule.rlog.ast.output;
 
+import org.mulgara.krule.rlog.ast.CheckRule;
 import org.mulgara.krule.rlog.ast.Predicate;
 import org.mulgara.krule.rlog.ast.Rule;
 import org.mulgara.krule.rlog.parser.URIParseException;
+import org.mulgara.krule.rlog.rdf.RDFNode;
 
 import java.io.PrintStream;
+import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.List;
 
 /**
@@ -58,11 +62,18 @@
    * @param r The rule to print.
    */
   private void emitRule(PrintStream out, Rule r) throws URIParseException {
-    out.println("  <krule:Rule rdf:about=\"#" + r.getName() + "\">");
-    emitTriggers(out, r.getTriggers());
+    if (r instanceof CheckRule) {
+      out.println("  <krule:ConsistencyCheck rdf:about=\"#" + r.getName() + "\">");
+    } else {
+      out.println("  <krule:Rule rdf:about=\"#" + r.getName() + "\">");
+      emitTriggers(out, r.getTriggers());
+    }
     out.println("    <hasQuery>\n" +
                 "      <Query>");
-    emitSelection(out, r.getHead());
+
+    if (r instanceof CheckRule) emitSelection(out, r.getVariables()); 
+    else emitSelection(out, r.getHead());
+
     emitWhereClause(out, r.getBody(), r.getBodySubtractions());
     out.println("      </Query>\n" +
                 "    </hasQuery>\n" +
@@ -86,15 +97,42 @@
    * @param selection The selection that makes up the head of a rule.
    */
   private void emitSelection(PrintStream out, Predicate selection) throws URIParseException {
+    emitSelection(out, Collections.singletonList(selection));
+  }
+
+
+  /**
+   * Prints the head for a rule to a PrintStream.
+   * @param out The PrintStream to send the selection to.
+   * @param selection The list of predicates that makes up the head of a rule.
+   */
+  private void emitSelection(PrintStream out, List<Predicate> selection) throws URIParseException {
+    List<RDFNode> sel = new ArrayList<RDFNode>();
+    for (Predicate p: selection) {
+      sel.add(p.getSubject());
+      sel.add(p.getPredicate());
+      sel.add(p.getObject());
+    }
+    emitSelection(out, sel);
+  }
+
+
+  /**
+   * Prints the selection values to a PrintStream.
+   * @param out The PrintStream to send the selection to.
+   * @param sel The elements to be selected.
+   */
+  private void emitSelection(PrintStream out, Collection<? extends RDFNode> sel) throws URIParseException {
     out.println("        <selectionVariables>\n" +
-                "          <rdf:Seq>\n" +
-                "            <rdf:li rdf:resource=\"" + selection.getSubject().getRdfLabel() + "\"/>\n" +
-                "            <rdf:li rdf:resource=\"" + selection.getPredicate().getRdfLabel() + "\"/>\n" +
-                "            <rdf:li rdf:resource=\"" + selection.getObject().getRdfLabel() + "\"/>\n" +
-                "          </rdf:Seq>\n" +
+                "          <rdf:Seq>");
+    for (RDFNode s: sel) {
+      out.println("            <rdf:li rdf:resource=\"" + s.getRdfLabel() + "\"/>");
+    }
+    out.println("          </rdf:Seq>\n" +
                 "        </selectionVariables>");
   }
 
+
   /**
    * Prints the body for a rule to a PrintStream.
    * @param out The PrintStream to send the where clause to.
@@ -112,13 +150,13 @@
   private void emitSubtractions(PrintStream out, List<Predicate> body, List<Predicate> subs, int indent) throws URIParseException {
     int lastElt = subs.size() - 1;
     out.println(sp(indent) + "<Difference>\n" +
-                sp(indent + 1) + "<argument>");
+                sp(indent + 1) + "<minuend>");
     if (lastElt == 0) emitConjunction(out, body, indent + 2);
     else emitSubtractions(out, body, subs.subList(0, lastElt), indent + 2);
-    out.println(sp(indent + 1) + "</argument>\n" +
-                sp(indent + 1) + "<argument>");
+    out.println(sp(indent + 1) + "</minuend>\n" +
+                sp(indent + 1) + "<subtrahend>");
     emitSimpleConstraint(out, subs.get(lastElt), indent + 2);
-    out.println(sp(indent + 1) + "</argument>\n" +
+    out.println(sp(indent + 1) + "</subtrahend>\n" +
                 sp(indent) + "</Difference>");
   }
 

Modified: trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/output/TripleGenerator.java
===================================================================
--- trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/output/TripleGenerator.java	2009-03-24 01:12:37 UTC (rev 1625)
+++ trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/output/TripleGenerator.java	2009-03-24 18:42:49 UTC (rev 1626)
@@ -16,9 +16,9 @@
 
 package org.mulgara.krule.rlog.ast.output;
 
-import static org.mulgara.krule.KruleLoader.KRULE;
-import static org.mulgara.krule.KruleLoader.LITERAL;
-import static org.mulgara.krule.KruleLoader.URI_REF;
+import static org.mulgara.query.rdf.Krule.KRULE;
+import static org.mulgara.query.rdf.Krule.LITERAL;
+import static org.mulgara.query.rdf.Krule.URI_REF;
 
 import java.net.URI;
 import java.net.URISyntaxException;

Modified: trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/parser/NSUtils.java
===================================================================
--- trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/parser/NSUtils.java	2009-03-24 01:12:37 UTC (rev 1625)
+++ trunk/src/jar/content-rlog/java/org/mulgara/krule/rlog/parser/NSUtils.java	2009-03-24 18:42:49 UTC (rev 1626)
@@ -18,8 +18,10 @@
 
 import java.net.URI;
 import java.net.URISyntaxException;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Set;
 
 import org.mulgara.krule.rlog.rdf.RDF;
 
@@ -130,4 +132,13 @@
   public static String getRegisteredNamespace(String domain) {
     return registeredDomains.get(domain);
   }
+
+
+  /**
+   * Returns a list of entries in the registered domains.
+   * @return A Set of Map.Entries which map domain abbreviations to full namespaces.
+   */
+  public static Set<Map.Entry<String,String>> getRegisteredDomains() {
+    return Collections.unmodifiableSet(registeredDomains.entrySet());
+  }
 }

Modified: trunk/src/jar/content-rlog/javacc/org/mulgara/krule/rlog/RlogParser.java
===================================================================
--- trunk/src/jar/content-rlog/javacc/org/mulgara/krule/rlog/RlogParser.java	2009-03-24 01:12:37 UTC (rev 1625)
+++ trunk/src/jar/content-rlog/javacc/org/mulgara/krule/rlog/RlogParser.java	2009-03-24 18:42:49 UTC (rev 1626)
@@ -3,13 +3,12 @@
 
 import java.io.StringReader;
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 
 import org.mulgara.krule.rlog.ast.Axiom;
 import org.mulgara.krule.rlog.ast.BPredicate;
 import org.mulgara.krule.rlog.ast.BPredicateLiteral;
+import org.mulgara.krule.rlog.ast.CheckRule;
 import org.mulgara.krule.rlog.ast.IntegerLiteral;
 import org.mulgara.krule.rlog.ast.InvertedPredicate;
 import org.mulgara.krule.rlog.ast.Predicate;
@@ -27,18 +26,30 @@
   /** This context holds parse-specific domain mappings. */
   private ParseContext context = new ParseContext();
 
+  /** The program being built by the parser. */
+  private Program program = null;
+
   /**
    * Parse an entire document into statements.
-   * @param query The document as a string.
+   * @param doc The document as a string.
    * @return A list of Statements parsed from the document.
    * @throws ParseException Due to a syntactical or grammatical error in the query document.
    */
-  public static List<Statement> parse(String query) throws ParseException {
-    RlogParser parser = new RlogParser(new StringReader(query));
-    return parser.statements();
+  public static Program parse(String doc) throws ParseException {
+    RlogParser parser = new RlogParser(new StringReader(doc));
+    return parser.program();
   }
 
   /**
+   * Retrieves the program that has been built up by this parser.
+   * @return The program built by this parser.
+   */
+  public Program getProgram() throws ParseException {
+    if (program == null) program = program();
+    return program;
+  }
+
+  /**
    * Remove quotation marks from the front and back of a string.
    * @param str The string to unquote.
    * @return A string containing everything from <var>str</var> between the outermost quotes.
@@ -48,15 +59,16 @@
   }
 
 /* statements  ::= (statement)+ */
-  final public List<Statement> statements() throws ParseException {
-  List<Statement> sList = new ArrayList<Statement>();
+  final public Program program() throws ParseException {
   Statement s;
+  if (program == null) program = new Program();
     label_1:
     while (true) {
       s = statement();
-                   if (s != null) sList.add(s);
+                   if (s != null) program.add(s);
       switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
       case PREFIX:
+      case IMPORT:
       case IMPLIED_BY:
       case INVERT:
       case IDENTIFIER:
@@ -70,7 +82,7 @@
         break label_1;
       }
     }
-                                                       {if (true) return sList;}
+                                                         {if (true) return program;}
     throw new Error("Missing return statement in function");
   }
 
@@ -79,6 +91,10 @@
   String d, ns;
   Statement s;
     switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+    case IMPORT:
+      importDecl();
+                 {if (true) return null;}
+      break;
     case PREFIX:
       prefix();
              {if (true) return null;}
@@ -135,7 +151,7 @@
       jj_consume_token(IMPLIED_BY);
       body = predicateList();
       jj_consume_token(DOT);
-                                            {if (true) return new Rule(body, context);}
+                                            {if (true) return new CheckRule(body, context);}
       break;
     case INVERT:
     case IDENTIFIER:
@@ -385,6 +401,15 @@
                                     context.registerDomain(d, ns);
   }
 
+/* importDecl  := IMPORT uri DOT */
+  final public void importDecl() throws ParseException {
+  String u;
+    jj_consume_token(IMPORT);
+    u = uri();
+    jj_consume_token(DOT);
+                           program.addImport(u);
+  }
+
 /* dom              ::= DOMAIN */
   final public String dom() throws ParseException {
   Token d;
@@ -422,67 +447,6 @@
     finally { jj_save(2, xla); }
   }
 
-  final private boolean jj_3R_17() {
-    Token xsp;
-    xsp = jj_scanpos;
-    if (jj_3R_20()) jj_scanpos = xsp;
-    if (jj_scan_token(IDENTIFIER)) return true;
-    if (jj_scan_token(LPAR)) return true;
-    if (jj_3R_13()) return true;
-    if (jj_scan_token(COMMA)) return true;
-    if (jj_3R_21()) return true;
-    if (jj_scan_token(RPAR)) return true;
-    return false;
-  }
-
-  final private boolean jj_3_1() {
-    if (jj_3R_3()) return true;
-    return false;
-  }
-
-  final private boolean jj_3R_14() {
-    Token xsp;
-    xsp = jj_scanpos;
-    if (jj_3R_17()) {
-    jj_scanpos = xsp;
-    if (jj_3R_18()) return true;
-    }
-    return false;
-  }
-
-  final private boolean jj_3R_8() {
-    if (jj_scan_token(VARIABLE)) return true;
-    if (jj_scan_token(LPAR)) return true;
-    if (jj_3R_13()) return true;
-    if (jj_scan_token(RPAR)) return true;
-    return false;
-  }
-
-  final private boolean jj_3R_26() {
-    if (jj_scan_token(STRING_LITERAL)) return true;
-    return false;
-  }
-
-  final private boolean jj_3R_25() {
-    if (jj_scan_token(INTEGER_LITERAL)) return true;
-    return false;
-  }
-
-  final private boolean jj_3R_24() {
-    Token xsp;
-    xsp = jj_scanpos;
-    if (jj_3R_25()) {
-    jj_scanpos = xsp;
-    if (jj_3R_26()) return true;
-    }
-    return false;
-  }
-
-  final private boolean jj_3R_12() {
-    if (jj_3R_9()) return true;
-    return false;
-  }
-
   final private boolean jj_3R_7() {
     Token xsp;
     xsp = jj_scanpos;
@@ -535,6 +499,11 @@
     return false;
   }
 
+  final private boolean jj_3R_9() {
+    if (jj_scan_token(DOMAIN)) return true;
+    return false;
+  }
+
   final private boolean jj_3R_6() {
     Token xsp;
     xsp = jj_scanpos;
@@ -576,11 +545,6 @@
     return false;
   }
 
-  final private boolean jj_3R_9() {
-    if (jj_scan_token(DOMAIN)) return true;
-    return false;
-  }
-
   final private boolean jj_3_3() {
     Token xsp;
     xsp = jj_scanpos;
@@ -623,6 +587,67 @@
     return false;
   }
 
+  final private boolean jj_3R_17() {
+    Token xsp;
+    xsp = jj_scanpos;
+    if (jj_3R_20()) jj_scanpos = xsp;
+    if (jj_scan_token(IDENTIFIER)) return true;
+    if (jj_scan_token(LPAR)) return true;
+    if (jj_3R_13()) return true;
+    if (jj_scan_token(COMMA)) return true;
+    if (jj_3R_21()) return true;
+    if (jj_scan_token(RPAR)) return true;
+    return false;
+  }
+
+  final private boolean jj_3_1() {
+    if (jj_3R_3()) return true;
+    return false;
+  }
+
+  final private boolean jj_3R_14() {
+    Token xsp;
+    xsp = jj_scanpos;
+    if (jj_3R_17()) {
+    jj_scanpos = xsp;
+    if (jj_3R_18()) return true;
+    }
+    return false;
+  }
+
+  final private boolean jj_3R_8() {
+    if (jj_scan_token(VARIABLE)) return true;
+    if (jj_scan_token(LPAR)) return true;
+    if (jj_3R_13()) return true;
+    if (jj_scan_token(RPAR)) return true;
+    return false;
+  }
+
+  final private boolean jj_3R_26() {
+    if (jj_scan_token(STRING_LITERAL)) return true;
+    return false;
+  }
+
+  final private boolean jj_3R_25() {
+    if (jj_scan_token(INTEGER_LITERAL)) return true;
+    return false;
+  }
+
+  final private boolean jj_3R_24() {
+    Token xsp;
+    xsp = jj_scanpos;
+    if (jj_3R_25()) {
+    jj_scanpos = xsp;
+    if (jj_3R_26()) return true;
+    }
+    return false;
+  }
+
+  final private boolean jj_3R_12() {
+    if (jj_3R_9()) return true;
+    return false;
+  }
+
   public RlogParserTokenManager token_source;
   SimpleCharStream jj_input_stream;
   public Token token, jj_nt;
@@ -638,7 +663,7 @@
       jj_la1_0();
    }
    private static void jj_la1_0() {
-      jj_la1_0 = new int[] {0x5c06100,0x5c06000,0x5c06100,0x5c06000,0x4c04000,0x4000000,0x5800000,0x4000000,0x4c00000,0x4000000,0x4000000,0x5800000,0x7d00000,0x2100000,0x20000,};
+      jj_la1_0 = new int[] {0xb80c300,0xb80c000,0xb80c300,0xb80c000,0x9808000,0x8000000,0xb000000,0x8000000,0x9800000,0x8000000,0x8000000,0xb000000,0xfa00000,0x4200000,0x40000,};
    }
   final private JJCalls[] jj_2_rtns = new JJCalls[3];
   private boolean jj_rescan = false;
@@ -815,8 +840,8 @@
 
   public ParseException generateParseException() {
     jj_expentries.removeAllElements();
-    boolean[] la1tokens = new boolean[27];
-    for (int i = 0; i < 27; i++) {
+    boolean[] la1tokens = new boolean[28];
+    for (int i = 0; i < 28; i++) {
       la1tokens[i] = false;
     }
     if (jj_kind >= 0) {
@@ -832,7 +857,7 @@
         }
       }
     }
-    for (int i = 0; i < 27; i++) {
+    for (int i = 0; i < 28; i++) {
       if (la1tokens[i]) {
         jj_expentry = new int[1];
         jj_expentry[0] = i;

Modified: trunk/src/jar/content-rlog/javacc/org/mulgara/krule/rlog/RlogParser.jj
===================================================================
--- trunk/src/jar/content-rlog/javacc/org/mulgara/krule/rlog/RlogParser.jj	2009-03-24 01:12:37 UTC (rev 1625)
+++ trunk/src/jar/content-rlog/javacc/org/mulgara/krule/rlog/RlogParser.jj	2009-03-24 18:42:49 UTC (rev 1626)
@@ -13,13 +13,12 @@
 
 import java.io.StringReader;
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 
 import org.mulgara.krule.rlog.ast.Axiom;
 import org.mulgara.krule.rlog.ast.BPredicate;
 import org.mulgara.krule.rlog.ast.BPredicateLiteral;
+import org.mulgara.krule.rlog.ast.CheckRule;
 import org.mulgara.krule.rlog.ast.IntegerLiteral;
 import org.mulgara.krule.rlog.ast.InvertedPredicate;
 import org.mulgara.krule.rlog.ast.Predicate;
@@ -37,18 +36,30 @@
   /** This context holds parse-specific domain mappings. */
   private ParseContext context = new ParseContext();
 
+  /** The program being built by the parser. */
+  private Program program = null;
+
   /**
    * Parse an entire document into statements.
-   * @param query The document as a string.
+   * @param doc The document as a string.
    * @return A list of Statements parsed from the document.
    * @throws ParseException Due to a syntactical or grammatical error in the query document.
    */
-  public static List<Statement> parse(String query) throws ParseException {
-    RlogParser parser = new RlogParser(new StringReader(query));
-    return parser.statements();
+  public static Program parse(String doc) throws ParseException {
+    RlogParser parser = new RlogParser(new StringReader(doc));
+    return parser.program();
   }
 
   /**
+   * Retrieves the program that has been built up by this parser.
+   * @return The program built by this parser.
+   */
+  public Program getProgram() throws ParseException {
+    if (program == null) program = program();
+    return program;
+  }
+
+  /**
    * Remove quotation marks from the front and back of a string.
    * @param str The string to unquote.
    * @return A string containing everything from <var>str</var> between the outermost quotes.
@@ -67,9 +78,10 @@
 <IN_COMMENT>
 SPECIAL_TOKEN : { < COMMENT: (~["\r","\n"])* > : DEFAULT }
 
-TOKEN [IGNORE_CASE] :
+TOKEN :
 {
     < PREFIX : "@prefix" >
+  | < IMPORT : "@import" >
 }
 
 TOKEN :
@@ -118,11 +130,11 @@
 
 
 /* statements  ::= (statement)+ */
-List<Statement> statements() : {
-  List<Statement> sList = new ArrayList<Statement>();
+Program program() : {
   Statement s;
+  if (program == null) program = new Program();
 } {
-  (s=statement() { if (s != null) sList.add(s); } )+ { return sList; }
+  (s=statement() { if (s != null) program.add(s); } )+ { return program; }
 }
 
 
@@ -131,7 +143,8 @@
   String d, ns;
   Statement s;
 } {
-  prefix() { return null; }
+  importDecl() { return null; }
+| prefix() { return null; }
 | ( LOOKAHEAD(10) s=axiom() | s=rule() ) { return s; }
 }
 
@@ -148,7 +161,7 @@
   List<Predicate> body;
   Predicate head;
 } {
-  <IMPLIED_BY> body=predicateList() <DOT> { return new Rule(body, context); }
+  <IMPLIED_BY> body=predicateList() <DOT> { return new CheckRule(body, context); }
 | head=predicate() <IMPLIED_BY> body=predicateList() <DOT> { return new Rule(head, body, context); }
 }
 
@@ -240,6 +253,14 @@
 }
 
 
+/* importDecl  := IMPORT uri DOT */
+void importDecl() : {
+  String u;
+} {
+  <IMPORT> u=uri() <DOT> { program.addImport(u); }
+}
+
+
 /* dom              ::= DOMAIN */
 String dom() : {
   Token d;

Modified: trunk/src/jar/content-rlog/javacc/org/mulgara/krule/rlog/RlogParserConstants.java
===================================================================
--- trunk/src/jar/content-rlog/javacc/org/mulgara/krule/rlog/RlogParserConstants.java	2009-03-24 01:12:37 UTC (rev 1625)
+++ trunk/src/jar/content-rlog/javacc/org/mulgara/krule/rlog/RlogParserConstants.java	2009-03-24 18:42:49 UTC (rev 1626)
@@ -6,24 +6,25 @@
   int EOF = 0;
   int COMMENT = 7;
   int PREFIX = 8;
-  int LOWER_CASE_LETTER = 9;
-  int UPPER_CASE_LETTER = 10;
-  int DIGIT = 11;
-  int J_LETTER_DIGIT = 12;
-  int IMPLIED_BY = 13;
-  int INVERT = 14;
-  int NEGATE = 15;
-  int DOT = 16;
-  int COMMA = 17;
-  int LPAR = 18;
-  int RPAR = 19;
-  int STRING_LITERAL = 20;
-  int URI = 21;
-  int IDENTIFIER = 22;
-  int VARIABLE = 23;
-  int TYPE = 24;
-  int INTEGER_LITERAL = 25;
-  int DOMAIN = 26;
+  int IMPORT = 9;
+  int LOWER_CASE_LETTER = 10;
+  int UPPER_CASE_LETTER = 11;
+  int DIGIT = 12;
+  int J_LETTER_DIGIT = 13;
+  int IMPLIED_BY = 14;
+  int INVERT = 15;
+  int NEGATE = 16;
+  int DOT = 17;
+  int COMMA = 18;
+  int LPAR = 19;
+  int RPAR = 20;
+  int STRING_LITERAL = 21;
+  int URI = 22;
+  int IDENTIFIER = 23;
+  int VARIABLE = 24;
+  int TYPE = 25;
+  int INTEGER_LITERAL = 26;
+  int DOMAIN = 27;
 
   int DEFAULT = 0;
   int IN_COMMENT = 1;
@@ -38,6 +39,7 @@
     "\"--\"",
     "<COMMENT>",
     "\"@prefix\"",
+    "\"@import\"",
     "<LOWER_CASE_LETTER>",
     "<UPPER_CASE_LETTER>",
     "<DIGIT>",

Modified: trunk/src/jar/content-rlog/javacc/org/mulgara/krule/rlog/RlogParserTokenManager.java
===================================================================
--- trunk/src/jar/content-rlog/javacc/org/mulgara/krule/rlog/RlogParserTokenManager.java	2009-03-24 01:12:37 UTC (rev 1625)
+++ trunk/src/jar/content-rlog/javacc/org/mulgara/krule/rlog/RlogParserTokenManager.java	2009-03-24 18:42:49 UTC (rev 1626)
@@ -2,12 +2,11 @@
 package org.mulgara.krule.rlog;
 import java.io.StringReader;
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 import org.mulgara.krule.rlog.ast.Axiom;
 import org.mulgara.krule.rlog.ast.BPredicate;
 import org.mulgara.krule.rlog.ast.BPredicateLiteral;
+import org.mulgara.krule.rlog.ast.CheckRule;
 import org.mulgara.krule.rlog.ast.IntegerLiteral;
 import org.mulgara.krule.rlog.ast.InvertedPredicate;
 import org.mulgara.krule.rlog.ast.Predicate;
@@ -24,53 +23,71 @@
 {
   public  java.io.PrintStream debugStream = System.out;
   public  void setDebugStream(java.io.PrintStream ds) { debugStream = ds; }
+private final int jjStopStringLiteralDfa_0(int pos, long active0)
+{
+   switch (pos)
+   {
+      case 0:
+         if ((active0 & 0x4000L) != 0L)
+         {
+            jjmatchedKind = 27;
+            return -1;
+         }
+         return -1;
+      case 1:
+         if ((active0 & 0x4000L) != 0L)
+         {
+            if (jjmatchedPos == 0)
+            {
+               jjmatchedKind = 27;
+               jjmatchedPos = 0;
+            }
+            return -1;
+         }
+         return -1;
+      default :
+         return -1;
+   }
+}
+private final int jjStartNfa_0(int pos, long active0)
+{
+   return jjMoveNfa_0(jjStopStringLiteralDfa_0(pos, active0), pos + 1);
+}
 private final int jjStopAtPos(int pos, int kind)
 {
    jjmatchedKind = kind;
    jjmatchedPos = pos;
    return pos + 1;
 }
+private final int jjStartNfaWithStates_0(int pos, int kind, int state)
+{
+   jjmatchedKind = kind;
+   jjmatchedPos = pos;
+   try { curChar = input_stream.readChar(); }
+   catch(java.io.IOException e) { return pos + 1; }
+   return jjMoveNfa_0(state, pos + 1);
+}
 private final int jjMoveStringLiteralDfa0_0()
 {
    switch(curChar)
    {
-      case 9:
-         jjmatchedKind = 1;
-         return jjMoveNfa_0(0, 0);
-      case 10:
-         jjmatchedKind = 2;
-         return jjMoveNfa_0(0, 0);
-      case 12:
-         jjmatchedKind = 4;
-         return jjMoveNfa_0(0, 0);
-      case 13:
-         jjmatchedKind = 3;
-         return jjMoveNfa_0(0, 0);
-      case 32:
-         jjmatchedKind = 5;
-         return jjMoveNfa_0(0, 0);
       case 40:
-         jjmatchedKind = 18;
-         return jjMoveNfa_0(0, 0);
+         return jjStopAtPos(0, 19);
       case 41:
-         jjmatchedKind = 19;
-         return jjMoveNfa_0(0, 0);
+         return jjStopAtPos(0, 20);
       case 44:
-         jjmatchedKind = 17;
-         return jjMoveNfa_0(0, 0);
+         return jjStopAtPos(0, 18);
       case 45:
-         jjmatchedKind = 15;
+         jjmatchedKind = 16;
          return jjMoveStringLiteralDfa1_0(0x40L);
       case 46:
-         jjmatchedKind = 16;
-         return jjMoveNfa_0(0, 0);
+         return jjStopAtPos(0, 17);
       case 58:
-         return jjMoveStringLiteralDfa1_0(0x2000L);
+         return jjMoveStringLiteralDfa1_0(0x4000L);
       case 64:
-         return jjMoveStringLiteralDfa1_0(0x100L);
+         return jjMoveStringLiteralDfa1_0(0x300L);
       case 126:
-         jjmatchedKind = 14;
-         return jjMoveNfa_0(0, 0);
+         return jjStopAtPos(0, 15);
       default :
          return jjMoveNfa_0(0, 0);
    }
@@ -79,135 +96,129 @@
 {
    try { curChar = input_stream.readChar(); }
    catch(java.io.IOException e) {
-   return jjMoveNfa_0(0, 0);
+      jjStopStringLiteralDfa_0(0, active0);
+      return 1;
    }
    switch(curChar)
    {
       case 45:
          if ((active0 & 0x40L) != 0L)
-         {
-            jjmatchedKind = 6;
-            jjmatchedPos = 1;
-         }
-         else if ((active0 & 0x2000L) != 0L)
-         {
-            jjmatchedKind = 13;
-            jjmatchedPos = 1;
-         }
+            return jjStopAtPos(1, 6);
+         else if ((active0 & 0x4000L) != 0L)
+            return jjStopAtPos(1, 14);
          break;
-      case 80:
-         return jjMoveStringLiteralDfa2_0(active0, 0x100L);
+      case 105:
+         return jjMoveStringLiteralDfa2_0(active0, 0x200L);
       case 112:
          return jjMoveStringLiteralDfa2_0(active0, 0x100L);
       default :
          break;
    }
-   return jjMoveNfa_0(0, 1);
+   return jjStartNfa_0(0, active0);
 }
 private final int jjMoveStringLiteralDfa2_0(long old0, long active0)
 {
    if (((active0 &= old0)) == 0L)
-      return jjMoveNfa_0(0, 1);
+      return jjStartNfa_0(0, old0); 
    try { curChar = input_stream.readChar(); }
    catch(java.io.IOException e) {
-   return jjMoveNfa_0(0, 1);
+      jjStopStringLiteralDfa_0(1, active0);
+      return 2;
    }
    switch(curChar)
    {
-      case 82:
-         return jjMoveStringLiteralDfa3_0(active0, 0x100L);
+      case 109:
+         return jjMoveStringLiteralDfa3_0(active0, 0x200L);
       case 114:
          return jjMoveStringLiteralDfa3_0(active0, 0x100L);
       default :
          break;
    }
-   return jjMoveNfa_0(0, 2);
+   return jjStartNfa_0(1, active0);
 }
 private final int jjMoveStringLiteralDfa3_0(long old0, long active0)
 {
    if (((active0 &= old0)) == 0L)
-      return jjMoveNfa_0(0, 2);
+      return jjStartNfa_0(1, old0); 
    try { curChar = input_stream.readChar(); }
    catch(java.io.IOException e) {
-   return jjMoveNfa_0(0, 2);
+      jjStopStringLiteralDfa_0(2, active0);
+      return 3;
    }
    switch(curChar)
    {
-      case 69:
-         return jjMoveStringLiteralDfa4_0(active0, 0x100L);
       case 101:
          return jjMoveStringLiteralDfa4_0(active0, 0x100L);
+      case 112:
+         return jjMoveStringLiteralDfa4_0(active0, 0x200L);
       default :
          break;
    }
-   return jjMoveNfa_0(0, 3);
+   return jjStartNfa_0(2, active0);
 }
 private final int jjMoveStringLiteralDfa4_0(long old0, long active0)
 {
    if (((active0 &= old0)) == 0L)
-      return jjMoveNfa_0(0, 3);
+      return jjStartNfa_0(2, old0); 
    try { curChar = input_stream.readChar(); }
    catch(java.io.IOException e) {
-   return jjMoveNfa_0(0, 3);
+      jjStopStringLiteralDfa_0(3, active0);
+      return 4;
    }
    switch(curChar)
    {
-      case 70:
-         return jjMoveStringLiteralDfa5_0(active0, 0x100L);
       case 102:
          return jjMoveStringLiteralDfa5_0(active0, 0x100L);
+      case 111:
+         return jjMoveStringLiteralDfa5_0(active0, 0x200L);
       default :
          break;
    }
-   return jjMoveNfa_0(0, 4);
+   return jjStartNfa_0(3, active0);
 }
 private final int jjMoveStringLiteralDfa5_0(long old0, long active0)
 {
    if (((active0 &= old0)) == 0L)
-      return jjMoveNfa_0(0, 4);
+      return jjStartNfa_0(3, old0); 
    try { curChar = input_stream.readChar(); }
    catch(java.io.IOException e) {
-   return jjMoveNfa_0(0, 4);
+      jjStopStringLiteralDfa_0(4, active0);
+      return 5;
    }
    switch(curChar)
    {
-      case 73:
-         return jjMoveStringLiteralDfa6_0(active0, 0x100L);
       case 105:
          return jjMoveStringLiteralDfa6_0(active0, 0x100L);
+      case 114:
+         return jjMoveStringLiteralDfa6_0(active0, 0x200L);
       default :
          break;
    }
-   return jjMoveNfa_0(0, 5);
+   return jjStartNfa_0(4, active0);
 }
 private final int jjMoveStringLiteralDfa6_0(long old0, long active0)
 {
    if (((active0 &= old0)) == 0L)
-      return jjMoveNfa_0(0, 5);
+      return jjStartNfa_0(4, old0); 
    try { curChar = input_stream.readChar(); }
    catch(java.io.IOException e) {
-   return jjMoveNfa_0(0, 5);
+      jjStopStringLiteralDfa_0(5, active0);
+      return 6;
    }
    switch(curChar)
    {
-      case 88:
-         if ((active0 & 0x100L) != 0L)
-         {
-            jjmatchedKind = 8;
-            jjmatchedPos = 6;
-         }
+      case 116:
+         if ((active0 & 0x200L) != 0L)
+            return jjStopAtPos(6, 9);
          break;
       case 120:
          if ((active0 & 0x100L) != 0L)
-         {
-            jjmatchedKind = 8;
-            jjmatchedPos = 6;
-         }
+            return jjStopAtPos(6, 8);
          break;
       default :
          break;
    }
-   return jjMoveNfa_0(0, 6);
+   return jjStartNfa_0(5, active0);
 }
 private final void jjCheckNAdd(int state)
 {
@@ -247,13 +258,6 @@
 };
 private final int jjMoveNfa_0(int startState, int curPos)
 {
-   int strKind = jjmatchedKind;
-   int strPos = jjmatchedPos;
-   int seenUpto;
-   input_stream.backup(seenUpto = curPos + 1);
-   try { curChar = input_stream.readChar(); }
-   catch(java.io.IOException e) { throw new Error("Internal Error"); }
-   curPos = 0;
    int[] nextStates;
    int startsAt = 0;
    jjnewStateCnt = 21;
@@ -274,19 +278,19 @@
                case 0:
                   if ((0x3fe000000000000L & l) != 0L)
                   {
-                     if (kind > 25)
-                        kind = 25;
+                     if (kind > 26)
+                        kind = 26;
                      jjCheckNAdd(14);
                   }
                   else if (curChar == 58)
                   {
-                     if (kind > 26)
-                        kind = 26;
+                     if (kind > 27)
+                        kind = 27;
                   }
                   else if (curChar == 48)
                   {
-                     if (kind > 25)
-                        kind = 25;
+                     if (kind > 26)
+                        kind = 26;
                   }
                   else if (curChar == 60)
                      jjCheckNAddTwoStates(10, 11);
@@ -302,8 +306,8 @@
                      jjCheckNAddStates(0, 2);
                   break;
                case 4:
-                  if (curChar == 34 && kind > 20)
-                     kind = 20;
+                  if (curChar == 34 && kind > 21)
+                     kind = 21;
                   break;
                case 5:
                   if ((0xff000000000000L & l) != 0L)
@@ -330,43 +334,43 @@
                      jjCheckNAddTwoStates(10, 11);
                   break;
                case 11:
-                  if (curChar == 62 && kind > 21)
-                     kind = 21;
+                  if (curChar == 62 && kind > 22)
+                     kind = 22;
                   break;
                case 12:
-                  if (curChar == 48 && kind > 25)
-                     kind = 25;
+                  if (curChar == 48 && kind > 26)
+                     kind = 26;
                   break;
                case 13:
                   if ((0x3fe000000000000L & l) == 0L)
                      break;
-                  if (kind > 25)
-                     kind = 25;
+                  if (kind > 26)
+                     kind = 26;
                   jjCheckNAdd(14);
                   break;
                case 14:
                   if ((0x3ff000000000000L & l) == 0L)
                      break;
-                  if (kind > 25)
-                     kind = 25;
+                  if (kind > 26)
+                     kind = 26;
                   jjCheckNAdd(14);
                   break;
                case 15:
-                  if (curChar == 58 && kind > 26)
-                     kind = 26;
+                  if (curChar == 58 && kind > 27)
+                     kind = 27;
                   break;
                case 17:
                   if ((0x3ff000000000000L & l) == 0L)
                      break;
-                  if (kind > 22)
-                     kind = 22;
+                  if (kind > 23)
+                     kind = 23;
                   jjstateSet[jjnewStateCnt++] = 17;
                   break;
                case 20:
                   if ((0x3ff000000000000L & l) == 0L)
                      break;
-                  if (kind > 24)
-                     kind = 24;
+                  if (kind > 25)
+                     kind = 25;
                   jjstateSet[jjnewStateCnt++] = 20;
                   break;
                default : break;
@@ -383,14 +387,14 @@
                case 0:
                   if ((0x7fffffeL & l) != 0L)
                   {
-                     if (kind > 23)
-                        kind = 23;
+                     if (kind > 24)
+                        kind = 24;
                      jjCheckNAdd(20);
                   }
                   else if ((0x7fffffe00000000L & l) != 0L)
                   {
-                     if (kind > 22)
-                        kind = 22;
+                     if (kind > 23)
+                        kind = 23;
                      jjCheckNAddStates(7, 9);
                   }
                   break;
@@ -412,15 +416,15 @@
                case 16:
                   if ((0x7fffffe00000000L & l) == 0L)
                      break;
-                  if (kind > 22)
-                     kind = 22;
+                  if (kind > 23)
+                     kind = 23;
                   jjCheckNAddStates(7, 9);
                   break;
                case 17:
                   if ((0x7fffffe87fffffeL & l) == 0L)
                      break;
-                  if (kind > 22)
-                     kind = 22;
+                  if (kind > 23)
+                     kind = 23;
                   jjCheckNAdd(17);
                   break;
                case 18:
@@ -430,15 +434,15 @@
                case 19:
                   if ((0x7fffffeL & l) == 0L)
                      break;
-                  if (kind > 23)
-                     kind = 23;
+                  if (kind > 24)
+                     kind = 24;
                   jjCheckNAdd(20);
                   break;
                case 20:
                   if ((0x7fffffe87fffffeL & l) == 0L)
                      break;
-                  if (kind > 24)
-                     kind = 24;
+                  if (kind > 25)
+                     kind = 25;
                   jjCheckNAdd(20);
                   break;
                default : break;
@@ -476,29 +480,10 @@
       }
       ++curPos;
       if ((i = jjnewStateCnt) == (startsAt = 21 - (jjnewStateCnt = startsAt)))
-         break;
+         return curPos;
       try { curChar = input_stream.readChar(); }
-      catch(java.io.IOException e) { break; }
+      catch(java.io.IOException e) { return curPos; }
    }
-   if (jjmatchedPos > strPos)
-      return curPos;
-
-   int toRet = Math.max(curPos, seenUpto);
-
-   if (curPos < toRet)
-      for (i = toRet - Math.min(curPos, seenUpto); i-- > 0; )
-         try { curChar = input_stream.readChar(); }
-         catch(java.io.IOException e) { throw new Error("Internal Error : Please send a bug report."); }
-
-   if (jjmatchedPos < strPos)
-   {
-      jjmatchedKind = strKind;
-      jjmatchedPos = strPos;
-   }
-   else if (jjmatchedPos == strPos && jjmatchedKind > strKind)
-      jjmatchedKind = strKind;
-
-   return toRet;
 }
 private final int jjMoveStringLiteralDfa0_1()
 {
@@ -599,19 +584,19 @@
    }
 }
 public static final String[] jjstrLiteralImages = {
-"", null, null, null, null, null, null, null, null, null, null, null, null, 
-"\72\55", "\176", "\55", "\56", "\54", "\50", "\51", null, null, null, null, null, null, 
-null, };
+"", null, null, null, null, null, null, null, "\100\160\162\145\146\151\170", 
+"\100\151\155\160\157\162\164", null, null, null, null, "\72\55", "\176", "\55", "\56", "\54", "\50", "\51", 
+null, null, null, null, null, null, null, };
 public static final String[] lexStateNames = {
    "DEFAULT", 
    "IN_COMMENT", 
 };
 public static final int[] jjnewLexState = {
    -1, -1, -1, -1, -1, -1, 1, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 
-   -1, -1, 
+   -1, -1, -1, 
 };
 static final long[] jjtoToken = {
-   0x7ffe101L, 
+   0xfffc301L, 
 };
 static final long[] jjtoSkip = {
    0xbeL, 
@@ -726,6 +711,11 @@
      switch(curLexState)
      {
        case 0:
+         try { input_stream.backup(0);
+            while (curChar <= 32 && (0x100003600L & (1L << curChar)) != 0L)
+               curChar = input_stream.BeginToken();
+         }
+         catch (java.io.IOException e1) { continue EOFLoop; }
          jjmatchedKind = 0x7fffffff;
          jjmatchedPos = 0;
          curPos = jjMoveStringLiteralDfa0_0();

Copied: trunk/src/jar/krule/java/org/mulgara/krule/ConsistencyCheck.java (from rev 1625, branches/consistency/src/jar/krule/java/org/mulgara/krule/ConsistencyCheck.java)
===================================================================
--- trunk/src/jar/krule/java/org/mulgara/krule/ConsistencyCheck.java	                        (rev 0)
+++ trunk/src/jar/krule/java/org/mulgara/krule/ConsistencyCheck.java	2009-03-24 18:42:49 UTC (rev 1626)
@@ -0,0 +1,139 @@
+/*
+ * 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.krule;
+
+import java.util.Collections;
+import java.util.Set;
+
+import org.apache.log4j.Logger;
+import org.mulgara.query.Answer;
+import org.mulgara.query.QueryException;
+import org.mulgara.query.TuplesException;
+import org.mulgara.query.Variable;
+import org.mulgara.resolver.OperationContext;
+import org.mulgara.resolver.spi.Resolver;
+import org.mulgara.resolver.spi.ResolverException;
+import org.mulgara.resolver.spi.SystemResolver;
+
+/**
+ * A rule that generates no data, but instead checks that the data is consistent.
+ *
+ * @created Mar 18, 2009
+ * @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 ConsistencyCheck extends Rule {
+
+  /** Serialization ID */
+  private static final long serialVersionUID = 5514372363770138432L;
+
+  /** Logger.  */
+  private static Logger logger = Logger.getLogger(ConsistencyCheck.class.getName());
+
+
+  /**
+   * Creates the consistency test as a rule.
+   * @param name The name of this rule.
+   */
+  public ConsistencyCheck(String name) {
+    super(name);
+  }
+
+
+  /**
+   * Adds a target for triggering. These are not legal for consistency checks.
+   * @param target The rule to be triggered when this rule is executed.
+   */
+  public void addTriggerTarget(Rule target) {
+    throw new IllegalStateException("Consistency checks cannot trigger other rules.");
+  }
+
+
+  /**
+   * Retrieves the list of subordinate rules.
+   * @return an immutable set of the subordinate rules.
+   */
+  public Set<Rule> getTriggerTargets() {
+    return Collections.emptySet();
+  }
+
+
+  /**
+   * Sets the query for this rule.
+   * @param queryStruct The query which retrieves data for this rule.
+   */
+  public void setQueryStruct(QueryStruct queryStruct) throws KruleStructureException {
+    this.query = queryStruct.extractQuery();
+  }
+
+
+  /**
+   * Runs this test.
+   * TODO: count the size of each individual constraint
+   * 
+   * @param context The context to query against.
+   * @param resolver The resolver to add data with.
+   * @param sysResolver The resolver to localize data with.
+   */
+  public void execute(OperationContext context, Resolver resolver, SystemResolver sysResolver) throws QueryException, TuplesException, ResolverException {
+    // Tests the output of this rule
+    Answer answer = null;
+    logger.debug("Running consistency check: " + name);
+    try {
+      answer = context.doQuery(query);
+    } catch (Exception e) {
+      throw new QueryException("Unable to access data in rule.", e);
+    }
+    try {
+      // compare the size of the result data
+      long c = answer.getRowCount();
+      if (0 != c) {
+        if (logger.isDebugEnabled()) {
+          logger.debug("Failed consistency check: " + name);
+          logOutput(answer);
+        }
+        throw new QueryException("Consistency check failed for rule \"" + name + "\". Got " + c + " failure results.");
+      }
+    } finally {
+      answer.close();
+    }
+  }
+
+
+  /**
+   * Send the result of a query to the logger.
+   * @param ans The result of the query to log.
+   */
+  private void logOutput(Answer ans) {
+    try {
+      ans.beforeFirst();
+      Variable[] vars = ans.getVariables();
+      StringBuilder line = new StringBuilder();
+      for (Variable v: vars) line.append(v).append(" ");
+      logger.debug(line.toString());
+      line = new StringBuilder();
+      while (ans.next()) {
+        for (int c = 0; c < vars.length; c++) {
+          if (c != 0) line.append(", ");
+          line.append(ans.getObject(c));
+        }
+        line.append("\n");
+      }
+      logger.debug(line);
+    } catch (TuplesException e) {
+      logger.error("Error reading failure in consistency check.", e);
+    }
+  }
+
+}

Modified: trunk/src/jar/krule/java/org/mulgara/krule/KruleLoader.java
===================================================================
--- trunk/src/jar/krule/java/org/mulgara/krule/KruleLoader.java	2009-03-24 01:12:37 UTC (rev 1625)
+++ trunk/src/jar/krule/java/org/mulgara/krule/KruleLoader.java	2009-03-24 18:42:49 UTC (rev 1626)
@@ -24,16 +24,20 @@
 // Third party packages
 import org.apache.log4j.Logger;
 import org.jrdf.graph.*;
+import org.jrdf.vocabulary.RDF;
 
 // Locally written packages
-import org.mulgara.itql.*;
 import org.mulgara.query.*;
 import org.mulgara.query.rdf.LiteralImpl;
+import org.mulgara.query.rdf.Mulgara;
 import org.mulgara.query.rdf.TripleImpl;
 import org.mulgara.query.rdf.URIReferenceImpl;
 import org.mulgara.resolver.OperationContext;
 import org.mulgara.rules.*;
+import org.mulgara.util.functional.Pair;
+
 import static org.mulgara.query.rdf.Mulgara.PREFIX_GRAPH;
+import static org.mulgara.query.rdf.Krule.*;
 
 /**
  * This object is used for parsing an RDF graph and building a rules structure
@@ -54,20 +58,17 @@
   /** The database session for querying. */
   private OperationContext operationContext;
 
-  /** The interpreter for parsing queries. */
-  private TqlInterpreter interpreter;
-
   /** The rules. */
   private RuleStructure rules;
 
-  /** The URI of the model containing the rule data. */
-  private URI ruleModel;
+  /** The Graph resource represented by ruleGraphUri. */
+  private final GraphResource ruleGraph;
 
-  /** The URI of the model containing the base data. */
-  private URI baseModel;
+  /** The URI of the graph containing the base data. */
+  private URI baseGraphUri;
 
-  /** The URI of the model to receive the entailed data. */
-  private URI destModel;
+  /** The URI of the graph to receive the entailed data. */
+  private URI destGraphUri;
 
   /** A map of namespace names to the URIs. */
   private Map<String,URI> aliases;
@@ -84,175 +85,30 @@
   /** Map of Constraint nodes to the associated constraint object. */
   private Map<Node,ConstraintExpression> constraintMap;
 
-  /** URI for the Krule namespace. */
-  public static final String KRULE = "http://mulgara.org/owl/krule/#";
+  /** RDF reference for rdf:type. */
+  public static final URIReferenceImpl RDF_TYPE = new URIReferenceImpl(RDF.TYPE);
 
-  /** URI for a constraint subject. */
-  private static final String HAS_SUBJECT_STR = KRULE + "hasSubject";
+  /** RDF reference for rdf:value. */
+  public static final URIReferenceImpl RDF_VALUE = new URIReferenceImpl(RDF.VALUE);
 
-  /** URI for a constraint predicate. */
-  private static final String HAS_PREDICATE_STR = KRULE + "hasPredicate";
+  /** A field used in queries to indicate no prior constraints on the answer. */
+  private static final UnconstrainedAnswer UNCONSTRAINED = new UnconstrainedAnswer();
 
-  /** URI for a constraint object. */
-  private static final String HAS_OBJECT_STR = KRULE + "hasObject";
-
-  /** URI for a constraint model. */
-  private static final String HAS_GRAPH_STR = KRULE + "hasModel";
-
-  /** URI for a query property on rules. */
-  private static final String HAS_QUERY_STR = KRULE + "hasQuery";
-
-  /** URI for an axiom subject. */
-  private static final String AXIOM_SUBJECT_STR = KRULE + "subject";
-
-  /** URI for an axiom predicate. */
-  private static final String AXIOM_PREDICATE_STR = KRULE + "predicate";
-
-  /** URI for an axiom object. */
-  private static final String AXIOM_OBJECT_STR = KRULE + "object";
-
-  /** URI for rule triggering predicate. */
-  private static final String TRIGGERS_STR = KRULE + "triggers";
-
-  /** URI for selection variables in a query. */
-  private static final String SELECTION_VARS_STR = KRULE + "selectionVariables";
-
-  /** URI for constraints predicate in a query. */
-  private static final String HAS_WHERE_CLAUSE_STR = KRULE + "hasWhereClause";
-
-  /** URI for the argument property. */
-  private static final String ARGUMENT_STR = KRULE + "argument";
-
-  /** URI for the transitive constraint argument. */
-  private static final String TRANSITIVE_ARGUMENT_STR = KRULE + "transitiveArgument";
-
-  /** URI for the transitive constraint anchor argument. */
-  private static final String ANCHOR_ARGUMENT_STR = KRULE + "anchorArgument";
-
-  /** URI for the name argument. */
-  private static final String NAME_STR = KRULE + "name";
-
-  /** URI for the constraint conjunction type. */
-  private static final String CONSTRAINT_CONJUNCTION_STR = KRULE + "ConstraintConjunction";
-
-  /** URI for the constraint disjunction type. */
-  private static final String CONSTRAINT_DISJUNCTION_STR = KRULE + "ConstraintDisjunction";
-
-  /** URI for the simple constraint type. */
-  private static final String SIMPLE_CONSTRAINT_STR = KRULE + "SimpleConstraint";
-
-  /** URI for difference type. */
-  private static final String DIFFERENCE_STR = KRULE + "Difference";
-
-  /** URI for the Value type. */
-  private static final String URI_REF_STR = KRULE + "URIReference";
-
-  /** URI for the Variable type. */
-  private static final String VARIABLE_STR = KRULE + "Variable";
-
-  /** URI for the Variable type. */
-  private static final String LITERAL_STR = KRULE + "Literal";
-
-  /** URI for axiom type. */
-  private static final String AXIOM_STR = KRULE + "Axiom";
-
-  /** URI for rule type. */
-  private static final String RULE_STR = KRULE + "Rule";
-
-  /** URI for query type. */
-  private static final String QUERY_STR = KRULE + "Query";
-
-  /** RDF reference for constraint subject. */
-  public static final URIReference HAS_SUBJECT = new URIReferenceImpl(URI.create(HAS_SUBJECT_STR));
-
-  /** RDF reference for constraint predicate. */
-  public static final URIReference HAS_PREDICATE = new URIReferenceImpl(URI.create(HAS_PREDICATE_STR));
-
-  /** RDF reference for constraint object. */
-  public static final URIReference HAS_OBJECT = new URIReferenceImpl(URI.create(HAS_OBJECT_STR));
-
-  /** RDF reference for constraint model. */
-  public static final URIReference HAS_GRAPH = new URIReferenceImpl(URI.create(HAS_GRAPH_STR));
-
-  /** RDF reference for query property on rules. */
-  public static final URIReference HAS_QUERY = new URIReferenceImpl(URI.create(HAS_QUERY_STR));
-
-  /** RDF reference for axiom subject. */
-  public static final URIReference AXIOM_SUBJECT = new URIReferenceImpl(URI.create(AXIOM_SUBJECT_STR));
-
-  /** RDF reference for axiom predicate. */
-  public static final URIReference AXIOM_PREDICATE = new URIReferenceImpl(URI.create(AXIOM_PREDICATE_STR));
-
-  /** RDF reference for axiom object. */
-  public static final URIReference AXIOM_OBJECT = new URIReferenceImpl(URI.create(AXIOM_OBJECT_STR));
-
-  /** RDF reference for rule triggering predicate. */
-  public static final URIReference TRIGGERS = new URIReferenceImpl(URI.create(TRIGGERS_STR));
-
-  /** RDF reference for selection variables predicate. */
-  public static final URIReference SELECTION_VARS = new URIReferenceImpl(URI.create(SELECTION_VARS_STR));
-
-  /** RDF reference for hasWhereClause predicate. */
-  public static final URIReference HAS_WHERE_CLAUSE = new URIReferenceImpl(URI.create(HAS_WHERE_CLAUSE_STR));
-
-  /** RDF reference for the argument property. */
-  public static final URIReference ARGUMENT = new URIReferenceImpl(URI.create(ARGUMENT_STR));
-
-  /** RDF reference for the transitive constraint argument. */
-  public static final URIReference TRANSITIVE_ARGUMENT = new URIReferenceImpl(URI.create(TRANSITIVE_ARGUMENT_STR));
-
-  /** RDF reference for the transitive constraint anchor argument. */
-  public static final URIReference ANCHOR_ARGUMENT = new URIReferenceImpl(URI.create(ANCHOR_ARGUMENT_STR));
-
-  /** RDF reference for the name argument. */
-  public static final URIReference NAME = new URIReferenceImpl(URI.create(NAME_STR));
-
-  /** RDF reference for constraint conjunction class. */
-  public static final URIReference CONSTRAINT_CONJUNCTION = new URIReferenceImpl(URI.create(CONSTRAINT_CONJUNCTION_STR));
-
-  /** RDF reference for constraint disjunction class. */
-  public static final URIReference CONSTRAINT_DISJUNCTION = new URIReferenceImpl(URI.create(CONSTRAINT_DISJUNCTION_STR));
-
-  /** RDF reference for the simple constraint type. */
-  public static final URIReference SIMPLE_CONSTRAINT = new URIReferenceImpl(URI.create(SIMPLE_CONSTRAINT_STR));
-
-  /** RDF reference for the Difference type. */
-  public static final URIReference DIFFERENCE = new URIReferenceImpl(URI.create(DIFFERENCE_STR));
-
-  /** RDF reference for the Value type. */
-  public static final URIReference URI_REF = new URIReferenceImpl(URI.create(URI_REF_STR));
-
-  /** RDF reference for the Variable type. */
-  public static final URIReference VARIABLE = new URIReferenceImpl(URI.create(VARIABLE_STR));
-
-  /** RDF reference for the Literal type. */
-  public static final URIReference LITERAL = new URIReferenceImpl(URI.create(LITERAL_STR));
-
-  /** RDF reference for the Axiom type. */
-  public static final URIReference AXIOM = new URIReferenceImpl(URI.create(AXIOM_STR));
-
-  /** RDF reference for the Rule type. */
-  public static final URIReference RULE = new URIReferenceImpl(URI.create(RULE_STR));
-
-  /** RDF reference for the Query type. */
-  public static final URIReference QUERY = new URIReferenceImpl(URI.create(QUERY_STR));
-
-
   /**
    * Principle constructor.
    *
-   * @param ruleModel The name of the model with the rules to run.
-   * @param baseModel The name of the model with the base data.
-   * @param destModel The name of the model which will receive the entailed data.
+   * @param ruleGraphUri The name of the graph with the rules to run.
+   * @param baseGraphUri The name of the graph with the base data.
+   * @param destGraphUri The name of the graph which will receive the entailed data.
    */
-  KruleLoader(URI ruleModel, URI baseModel, URI destModel) {
-    this.ruleModel = ruleModel;
-    this.baseModel = baseModel;
-    this.destModel = destModel;
+  KruleLoader(URI ruleGraphUri, URI baseGraphUri, URI destGraphUri) {
+    this.baseGraphUri = baseGraphUri;
+    this.destGraphUri = destGraphUri;
 
+    ruleGraph = new GraphResource(ruleGraphUri);
+
     // set the query objects to null
     operationContext = null;
-    interpreter = null;
 
     // initialize the aliases
     newAliases();
@@ -279,16 +135,12 @@
    * Reads the ruleModel in the database and constructs the rules from it.
    *
    * @param opContextParam The operationContext for querying on.
-   * @param systemModel The system model.
    * @return A new rule structure.
    * @throws InitializerException There was a problem reading and creating the rules.
    */
-  public Rules readRules(Object opContextParam, URI systemModel) throws InitializerException, RemoteException {
+  public Rules readRules(Object opContextParam) throws InitializerException, RemoteException {
     this.operationContext = (OperationContext)opContextParam;
 
-    // get a new interpreter
-    interpreter = new TqlInterpreter(aliases);
-
     rules = null;
     try {
       if (logger.isDebugEnabled()) logger.debug("Initializing for rule queries.");
@@ -298,7 +150,7 @@
       if (logger.isDebugEnabled()) logger.debug("Querying for rules");
       rules = findRules();
       // set the target model
-      rules.setTargetModel(destModel);
+      rules.setTargetModel(destGraphUri);
 
       // find the triggers
       loadTriggers();
@@ -363,14 +215,18 @@
    * @throws QueryException When there is an exception finding the rules.
    */
   private RuleStructure findRules() throws QueryException, TuplesException {
-  	Query query;
-    try {
-      // find all of the rules
-      query = interpreter.parseQuery("select $rule from <" + ruleModel + "> where $rule <rdf:type> <krule:Rule> ;");
-    } catch (Exception e) {
-      throw new QueryException("Invalid query attempted while finding rules.", e);
-    }
-    
+    // select $rule from <ruleGraph> where $rule <rdf:type> <krule:Rule>
+    Variable ruleV = new Variable("rule");
+    Variable ruletypeV = new Variable("ruletype");
+    ConstraintExpression where = new ConstraintConjunction(
+        new ConstraintImpl(ruleV, RDF_TYPE, ruletypeV),
+        new ConstraintDisjunction(
+            new ConstraintIs(ruletypeV, RULE),
+            new ConstraintIs(ruletypeV, CHECK)
+        )
+    );
+    Query query = createQuery(where, ruleV, ruletypeV);
+
     Answer ruleAnswer = query(query);
     if (logger.isDebugEnabled()) logger.debug("Got response for rule query");
 
@@ -381,7 +237,11 @@
       // create all the rules
       while (ruleAnswer.next()) {
         // create the rule and add it to the set
-        rules.add(new Rule(ruleAnswer.getObject(0).toString()));
+        URIReference type = (URIReference)ruleAnswer.getObject(1);
+        String name = ruleAnswer.getObject(0).toString();
+        if (type.equals(RULE)) rules.add(new Rule(name));
+        else if (type.equals(CHECK)) rules.add(new ConsistencyCheck(name));
+        else throw new QueryException("Unexpected type for rule: " + name + "(" + type + ")");
       }
     } finally {
       ruleAnswer.close();
@@ -399,12 +259,10 @@
    * @throws InitializerException Data structures did not meet preconditions.
    */
   private void loadTriggers() throws QueryException, TuplesException, InitializerException {
-    Query query;
-    try {
-      query = interpreter.parseQuery("select $src $dest from <" + ruleModel + "> where $src <krule:triggers> $dest ;");
-    } catch (Exception e) {
-      throw new QueryException("Invalid query while finding triggers.", e);
-    }
+    // select $src $dest from <ruleGraph> where $src <krule:triggers> $dest
+    Variable srcV = new Variable("src");
+    Variable destV = new Variable("dest");
+    Query query = createQuery(new ConstraintImpl(srcV, TRIGGERS, destV), srcV, destV);
 
     Answer answer = query(query);
 
@@ -433,55 +291,66 @@
    */
   private void loadQueries() throws TuplesException, QueryException, KruleStructureException, InitializerException {
     if (logger.isDebugEnabled()) logger.debug("Loading Queries");
+
+    // create some of the loop-invariant resources
+    final URIReferenceImpl sysPrefix = new URIReferenceImpl(URI.create(PREFIX_GRAPH));
+    final URIReferenceImpl mulgaraPrefix = new URIReferenceImpl(Mulgara.PREFIX_URI);
+    final URIReferenceImpl seqPrefix = new URIReferenceImpl(URI.create(RDF.BASE_URI + "_"));
+
     // go through the rules to set their queries
     Iterator<Rule> ri = rules.getRuleIterator();
     while (ri.hasNext()) {
       Rule rule = ri.next();
 
+      // create a resource for this rule
+      URIReferenceImpl ruleRef = new URIReferenceImpl(URI.create(rule.getName()));
       if (logger.isDebugEnabled()) logger.debug("Reading query for rule: " + rule.getName());
-      Query query;
-      try {
-        // get the query data for this rule
-        query = interpreter.parseQuery("select $pre $v $t from <" + ruleModel +
-            "> where <" + rule.getName() + "> <krule:hasQuery> $q and $q <krule:selectionVariables> $vs and" +
-            " $vs $pre $v and $pre <mulgara:prefix> <rdf:_> in <"+ PREFIX_GRAPH +
-            "> and $v <rdf:type> $t ;");
-      } catch (Exception e) {
-        throw new QueryException("Invalid query while getting rule queries.", e);
-      }
+      // select $pre $v $t from <ruleGraph>
+      // where <#ruleRef> <krule:hasQuery> $q and $q <krule:selectionVariables> $vs
+      // and $vs $pre $v and $pre <mulgara:prefix> <rdf:_> in <sys:prefix> and $v <rdf:type> $t
+      Variable qV = new Variable("q");
+      Variable vsV = new Variable("vs");
+      Variable preV = new Variable("pre");
+      Variable vV = new Variable("v");
+      Variable tV = new Variable("t");
+      ConstraintExpression where = new ConstraintConjunction(
+          new ConstraintImpl(ruleRef, HAS_QUERY, qV),
+          new ConstraintImpl(qV, SELECTION_VARS, vsV),
+          new ConstraintImpl(vsV, preV, vV),
+          new ConstraintImpl(preV, mulgaraPrefix, seqPrefix, sysPrefix),
+          new ConstraintImpl(vV, RDF_TYPE, tV)
+      );
+      Query query = createQuery(where, preV, vV, tV);
       Answer answer = query(query);
 
       // get the length of the sequence prefix
       int prefixLength = ((URI)aliases.get("rdf")).toString().length() + 1;
       // get the variables and values as elements with the appropriate type
-      URIReference[] elements = new URIReference[3];
-      URIReference[] types = new URIReference[3];
+      List<URIReference> elements = new ArrayList<URIReference>();
+      List<URIReference> types = new ArrayList<URIReference>();
       try {
         while (answer.next()) {
           if (logger.isDebugEnabled()) logger.debug("Getting element from " + answer.getObject(0));
           // work out the position of the element.  Subject=0 Predicate=1 Object=2
           int seqNr = Integer.parseInt(answer.getObject(0).toString().substring(prefixLength)) - 1;
           if (logger.isDebugEnabled()) logger.debug("parsed: " + seqNr);
-          if (seqNr > elements.length) {
-            throw new KruleStructureException("Rule " + rule.getName() + " has too many insertion elements. Found sequence number: " + seqNr);
-          }
           // get the selection element and its type
-          elements[seqNr] = (URIReference)answer.getObject(1);
-          types[seqNr] = (URIReference)answer.getObject(2);
-          if (logger.isDebugEnabled()) logger.debug("Nr: " + seqNr + ", v: " + elements[seqNr] + ", type: " + types[seqNr]);
+          setList(elements, seqNr, (URIReference)answer.getObject(1));
+          setList(types, seqNr, (URIReference)answer.getObject(2));
+          if (logger.isDebugEnabled()) logger.debug("Nr: " + seqNr + ", v: " + elements.get(seqNr) + ", type: " + types.get(seqNr));
         }
       } finally {
         answer.close();
       }
-      for (int select = 0; select < elements.length; select++) {
-        if (elements[select] == null || types[select] == null) {
+      for (int select = 0; select < elements.size(); select++) {
+        if (elements.get(select) == null || types.get(select) == null) {
           // one element was set. Get a descriptive error message
           StringBuffer errorMsg = new StringBuffer();
-          for (int s = 0; s < elements.length; s++) {
-            if (elements[s] == null) errorMsg.append(" <null>");
-            else errorMsg.append(" ").append(elements[s]);
-            if (types[s] == null) errorMsg.append("^^<null>");
-            else errorMsg.append("^^<").append(types[s]).append(">");
+          for (int s = 0; s < elements.size(); s++) {
+            if (elements.get(s) == null) errorMsg.append(" <null>");
+            else errorMsg.append(" ").append(elements.get(s));
+            if (types.get(s) == null) errorMsg.append("^^<null>");
+            else errorMsg.append("^^<").append(types.get(s)).append(">");
           }
           throw new KruleStructureException("Rule " + rule.getName() + " does not have enough insertion elements. Got: " + errorMsg);
         }
@@ -490,13 +359,15 @@
       QueryStruct queryStruct = new QueryStruct(elements, types, aliases, uriReferences, varReferences, literalReferences);
 
       // read in the WHERE reference
-      try {
-        // get the WHERE clause for this rule
-        query = interpreter.parseQuery("select $w from <" + ruleModel +
-            "> where <" + rule.getName() + "> <krule:hasQuery> $q and $q <krule:hasWhereClause> $w;");
-      } catch (Exception e) {
-        throw new QueryException("Invalid query reading WHERE clause for rule: " + rule.getName(), e);
-      }
+
+      // select $w from <ruleGraph>
+      // where <#rule.getName()> <krule:hasQuery> $q and $q <krule:hasWhereClause> $w
+      Variable wV = new Variable("w");
+      where = new ConstraintConjunction(
+          new ConstraintImpl(ruleRef, HAS_QUERY, qV),
+          new ConstraintImpl(qV, HAS_WHERE_CLAUSE, wV)
+      );
+      query = createQuery(where, wV);
       answer = query(query);
 
       try {
@@ -518,7 +389,7 @@
 
       if (logger.isDebugEnabled()) logger.debug("Setting models for the query");
       // set the models
-      queryStruct.setModelExpression(baseModel, destModel);
+      queryStruct.setModelExpression(baseGraphUri, destGraphUri);
 
       if (logger.isDebugEnabled()) logger.debug("Setting query structure for the rule");
       // create a new query and set it for the rule
@@ -539,15 +410,20 @@
   private Set<org.jrdf.graph.Triple> findAxioms() throws TuplesException, QueryException, KruleStructureException, InitializerException {
     if (logger.isDebugEnabled()) logger.debug("Loading Axioms");
 
-    Query query;
-    try {
-      // get the query data for this rule
-      query = interpreter.parseQuery("select $s $p $o from <" + ruleModel +
-          "> where $axiom <rdf:type> <krule:Axiom> and $axiom <krule:subject> $s" +
-          " and $axiom <krule:predicate> $p and $axiom <krule:object> $o;");
-    } catch (Exception e) {
-      throw new QueryException("Invalid query while finding axioms.", e);
-    }
+    // select $s $p $o from <ruleGraph>
+    // where $axiom <rdf:type> <krule:Axiom> and $axiom <krule:subject> $s
+    // and $axiom <krule:predicate> $p and $axiom <krule:object> $o
+    Variable sV = new Variable("s");
+    Variable pV = new Variable("p");
+    Variable oV = new Variable("o");
+    Variable axiomV = new Variable("axiom");
+    ConstraintExpression where = new ConstraintConjunction(
+        new ConstraintImpl(axiomV, RDF_TYPE, AXIOM),
+        new ConstraintImpl(axiomV, AXIOM_SUBJECT, sV),
+        new ConstraintImpl(axiomV, AXIOM_PREDICATE, pV),
+        new ConstraintImpl(axiomV, AXIOM_OBJECT, oV)
+    );
+    Query query = createQuery(where, sV, pV, oV);
     Answer answer = query(query);
 
     // prepare the set of axioms
@@ -618,15 +494,15 @@
   private void findUriReferences() throws TuplesException, QueryException, InitializerException {
     if (logger.isDebugEnabled()) logger.debug("Querying for URI reference objects.");
 
-    Query query;
-    try {
-      // find the URI references and the referred URIs.
-      query = interpreter.parseQuery("select $ref $uri from <" +
-          ruleModel + "> where $ref <rdf:type> <krule:URIReference> and $ref <rdf:value> $uri ;");
-    } catch (Exception e) {
-      throw new QueryException("Invalid query while looking for URI references.", e);
-    }
-    
+    // select $ref $uri from <ruleGraph>
+    // where $ref <rdf:type> <krule:URIReference> and $ref <rdf:value> $uri
+    Variable refV = new Variable("ref");
+    Variable uriV = new Variable("uri");
+    ConstraintExpression where = new ConstraintConjunction(
+        new ConstraintImpl(refV, RDF_TYPE, URI_REF),
+        new ConstraintImpl(refV, RDF_VALUE, uriV)
+    );
+    Query query = createQuery(where, refV, uriV);
     Answer answer = query(query);
     if (logger.isDebugEnabled()) logger.debug("Found all URI references.");
 
@@ -657,15 +533,17 @@
   private void findVarReferences() throws TuplesException, QueryException, InitializerException {
     if (logger.isDebugEnabled()) logger.debug("Querying for variable reference objects.");
 
-    Query query;
-    try {
-      // find the URI references and the referred URIs.
-      query = interpreter.parseQuery("select $ref $name from <" +
-          ruleModel + "> where $ref <rdf:type> <krule:Variable> and $ref <krule:name> $name ;");
-    } catch (Exception e) {
-      throw new QueryException("Invalid query while finding variable references.", e);
-    }
-    
+    // find the URI references and the referred URIs.
+
+    // select $ref $name from <ruleGraph>
+    // where $ref <rdf:type> <krule:Variable> and $ref <krule:name> $name
+    Variable refV = new Variable("ref");
+    Variable nameV = new Variable("name");
+    ConstraintExpression where = new ConstraintConjunction(
+        new ConstraintImpl(refV, RDF_TYPE, VARIABLE),
+        new ConstraintImpl(refV, NAME, nameV)
+    );
+    Query query = createQuery(where, refV, nameV);
     Answer answer = query(query);
     if (logger.isDebugEnabled()) logger.debug("Found all variable references.");
 
@@ -677,7 +555,7 @@
         URIReference ref = (URIReference)answer.getObject(0);
         Literal name = (Literal)answer.getObject(1);
         if (logger.isDebugEnabled()) logger.debug("Mapping <" + ref + "> to <" + name + ">");
-        varReferences.put(ref, new Variable(name.toString()));
+        varReferences.put(ref, new Variable(name.getLexicalForm()));
       }
     } finally {
       answer.close();
@@ -696,15 +574,15 @@
   private void findLiteralReferences() throws TuplesException, QueryException, InitializerException {
     if (logger.isDebugEnabled()) logger.debug("Querying for Literal objects.");
 
-    Query query;
-    try {
-      // find the URI references and the referred URIs.
-      query = interpreter.parseQuery("select $lit $str from <" +
-          ruleModel + "> where $lit <rdf:type> <krule:Literal> and $lit <rdf:value> $str ;");
-    } catch (Exception e) {
-      throw new QueryException("Invalid query while looking for literal references.", e);
-    }
-    
+    // select $lit $str from <ruleGraph>
+    // where $lit <rdf:type> <krule:Literal> and $lit <rdf:value> $str
+    Variable litV = new Variable("lit");
+    Variable strV = new Variable("str");
+    ConstraintExpression where = new ConstraintConjunction(
+        new ConstraintImpl(litV, RDF_TYPE, LITERAL),
+        new ConstraintImpl(litV, RDF_VALUE, strV)
+    );
+    Query query = createQuery(where, litV, strV);
     Answer answer = query(query);
     if (logger.isDebugEnabled()) logger.debug("Found all Literals.");
 
@@ -735,16 +613,26 @@
   private void loadSimpleConstraints() throws KruleStructureException, TuplesException, QueryException {
     if (logger.isDebugEnabled()) logger.debug("Querying for Simple constraints.");
 
-    Query query;
-    try {
-      // find the URI references and the referred URIs.
-      query = interpreter.parseQuery("select $c $p $o from <" + ruleModel +
-          "> where $c <rdf:type> <krule:SimpleConstraint> and $c $p $o and " +
-          "($p <mulgara:is> <krule:hasSubject> or $p <mulgara:is> <krule:hasPredicate> or " +
-          "$p <mulgara:is> <krule:hasObject> or $p <mulgara:is> <krule:hasModel>);");
-    } catch (Exception e) {
-      throw new QueryException("Invalid query while looking for simple constraints.", e);
-    }
+    // select $c $p $o from <ruleGraph>
+    // where $c <rdf:type> <krule:SimpleConstraint> and $c $p $o
+    // and ($p <mulgara:is> <krule:hasSubject>
+    //   or $p <mulgara:is> <krule:hasPredicate>
+    //   or $p <mulgara:is> <krule:hasObject>
+    //   or $p <mulgara:is> <krule:hasModel>)
+    Variable cV = new Variable("c");
+    Variable pV = new Variable("p");
+    Variable oV = new Variable("o");
+    ConstraintExpression where = new ConstraintConjunction(
+        new ConstraintImpl(cV, RDF_TYPE, SIMPLE_CONSTRAINT),
+        new ConstraintImpl(cV, pV, oV),
+        new ConstraintDisjunction(
+            new ConstraintIs(pV, HAS_SUBJECT),
+            new ConstraintIs(pV, HAS_PREDICATE),
+            new ConstraintIs(pV, HAS_OBJECT),
+            new ConstraintIs(pV, HAS_GRAPH)
+        )
+    );
+    Query query = createQuery(where, cV, pV, oV);
 
     Answer answer = query(query);
     if (logger.isDebugEnabled()) logger.debug("Found all simple constraints.");
@@ -802,23 +690,42 @@
     // build constraints in place, recursively constructing child constraints until all are found
     if (logger.isDebugEnabled()) logger.debug("Querying for Join constraints.");
 
-    Query query;
-    try {
-      // find the URI references and the referred URIs.
-      query = interpreter.parseQuery("select $constraint $constraint2 $type from <" + ruleModel +
-          "> where $constraint <krule:argument> $constraint2 and $constraint <rdf:type> $type and " +
-          "($type <mulgara:is> <krule:ConstraintConjunction> or $type <mulgara:is> <krule:ConstraintDisjunction>);");
-    } catch (Exception e) {
-      throw new QueryException("Invalid query while looking for join constraints.", e);
-    }
+    // don't look for the type of the child constraints. krule:argument has range of Constraint.
 
+    // select $constraint $arg $constraint2 $type from <ruleGraph>
+    // where $constraint $arg $constraint2
+    //   and $constraint <rdf:type> $type
+    //   and ($type <mulgara:is> <krule:ConstraintConjunction>
+    //     or $type <mulgara:is> <krule:ConstraintDisjunction>)
+    //   and ($arg <mulgara:is> <krule:argument>
+    //     or $arg <mulgara:is> <krule:minuend>
+    //     or $arg <mulgara:is> <krule:subtrahend>)
+    Variable constraintV = new Variable("constraint");
+    Variable argV = new Variable("arg");
+    Variable constraint2V = new Variable("constraint2");
+    Variable typeV = new Variable("type");
+    ConstraintExpression where = new ConstraintConjunction(
+        new ConstraintImpl(constraintV, argV, constraint2V),
+        new ConstraintImpl(constraintV, RDF_TYPE, typeV),
+        new ConstraintDisjunction(
+            new ConstraintIs(typeV, CONSTRAINT_CONJUNCTION),
+            new ConstraintIs(typeV, CONSTRAINT_DISJUNCTION),
+            new ConstraintIs(typeV, DIFFERENCE)
+        ),
+        new ConstraintDisjunction(
+            new ConstraintIs(argV, ARGUMENT),
+            new ConstraintIs(argV, MINUEND),
+            new ConstraintIs(argV, SUBTRAHEND)
+        )
+    );
+    Query query = createQuery(where, constraintV, argV, constraint2V, typeV);
     Answer answer = query(query);
     if (logger.isDebugEnabled()) logger.debug("Found all join constraints.");
 
     // accumulate all the constraint links and types
 
     // create a map of join constraints to the constraints that they join
-    Map<Node,Set<Node>> constraintLinks = new HashMap<Node,Set<Node>>();
+    Map<Node,Set<Pair<Node,Node>>> constraintLinks = new HashMap<Node,Set<Pair<Node,Node>>>();
 
     // map the join constraints to the type of join
     Map<Node,URIReference> joinTypes = new HashMap<Node,URIReference>();
@@ -827,13 +734,16 @@
       // map each reference to the associated argument and type
       while (answer.next()) {
         Node constraintNode = (Node)answer.getObject(0);
-        Node constraintNode2 = (Node)answer.getObject(1);
-        URIReference type = (URIReference)answer.getObject(2);
-        if (logger.isDebugEnabled()) logger.debug("constraint (" + type + ")<" + constraintNode + "> -> <" + constraintNode2 + ">");
-        // map the constraint to its argument
-        addLink(constraintLinks, constraintNode, constraintNode2);
+        URIReference arg = (URIReference)answer.getObject(1);
+        Node constraintNode2 = (Node)answer.getObject(2);
+        URIReference type = (URIReference)answer.getObject(3);
+        if (logger.isDebugEnabled()) logger.debug("constraint (" + type + ")<" + constraintNode + ">  <" + arg + "><" + constraintNode2 + ">");
+        // map the constraint to its operand: constraintNode2
+        addLink(constraintLinks, constraintNode, new Pair<Node,Node>(arg, constraintNode2));
         // map the type
-        joinTypes.put(constraintNode, type);
+        URIReference storedType = joinTypes.get(constraintNode);
+        if (storedType == null) joinTypes.put(constraintNode, type);
+        else if (!storedType.equals(type)) throw new KruleStructureException("Varying types in constraint operations in the rule structure");
       }
     } finally {
       answer.close();
@@ -841,19 +751,19 @@
 
     if (logger.isDebugEnabled()) logger.debug("mapping join constraint RDF nodes to join constraint objects");
     // collect all arguments together into constraints and map the node to the constraint
-    for (Map.Entry<Node,Set<Node>> entry: constraintLinks.entrySet()) {
+    for (Map.Entry<Node,Set<Pair<Node,Node>>> entry: constraintLinks.entrySet()) {
       // get the constraint node in question
       Node constraintNode = entry.getKey();
       // see if it maps to a constraint
       if (constraintMap.get(constraintNode) == null) {
         // the constraint does not exist
         // get the argument nodes
-        Set<Node> args = entry.getValue();
+        Set<Pair<Node,Node>> operands = entry.getValue();
         // get the constraint's type
         Node type = joinTypes.get(constraintNode);
-        if (type == null) throw new KruleStructureException("No type (AND/OR) available on join constraint: " + constraintNode);
+        if (type == null) throw new KruleStructureException("No type (AND/OR/Minus) available on join constraint: " + constraintNode);
         // convert the RDF nodes to constraints
-        List<ConstraintExpression> constraintArgs = getConstraints(args, constraintLinks, joinTypes);
+        List<ConstraintExpression> constraintArgs = getConstraints(operands, constraintLinks, joinTypes);
         ConstraintExpression joinConstraint = newJoinConstraint(type, constraintArgs);
         if (logger.isDebugEnabled()) logger.debug("mapped " + constraintNode + " -> " + joinConstraint);
         // build the join constraint, and map the node to it
@@ -877,16 +787,12 @@
   private void loadHavingConstraints() throws KruleStructureException, TuplesException, QueryException {
     if (logger.isDebugEnabled()) logger.debug("Querying for Having constraints.");
 
-    Query query;
-    try {
-      // find the URI references and the referred URIs.
-      query = interpreter.parseQuery("select $constraint from <" + ruleModel +
-          "> where $rule <krule:hasHavingClause> $constraint;");
-    } catch (Exception e) {
-      throw new QueryException("Invalid query while searching on having clauses.", e);
-    }
-
+    // select $constraint from <ruleGraph> where $rule <krule:hasHavingClause> $constraint
+    Variable ruleV = new Variable("rule");
+    Variable constraintV = new Variable("constraint");
+    Query query = createQuery(new ConstraintImpl(ruleV, HAS_HAVING_CLAUSE, constraintV), constraintV);
     Answer answer = query(query);
+
     if (logger.isDebugEnabled()) logger.debug("Found all having constraints.");
 
     try {
@@ -907,15 +813,22 @@
   private void loadTransitiveConstraints() throws KruleStructureException, TuplesException, QueryException {
     if (logger.isDebugEnabled()) logger.debug("Querying for Transitive constraints.");
 
-    Query query;
-    try {
-      // find the URI references and the referred URIs.
-      query = interpreter.parseQuery("select $c $p $arg from <" + ruleModel +
-          "> where $c <rdf:type> <krule:TransitiveConstraint> and $c $p $arg and " +
-          "($p <mulgara:is> <krule:transitiveArgument> or $p <mulgara:is> <krule:anchorArgument>);");
-    } catch (Exception e) {
-      throw new QueryException("Invalid query while querying for transitive constraints.", e);
-    }
+    // select $c $p $arg from <ruleGraph>
+    // where $c <rdf:type> <krule:TransitiveConstraint> and $c $p $arg
+    // and ($p <mulgara:is> <krule:transitiveArgument>
+    //   or $p <mulgara:is> <krule:anchorArgument>)
+    Variable cV = new Variable("c");
+    Variable pV = new Variable("p");
+    Variable argV = new Variable("arg");
+    ConstraintExpression where = new ConstraintConjunction(
+        new ConstraintImpl(cV, RDF_TYPE, TRANSITIVE_CONSTRAINT),
+        new ConstraintImpl(cV, pV, argV),
+        new ConstraintDisjunction(
+            new ConstraintIs(pV, TRANSITIVE_ARGUMENT),
+            new ConstraintIs(pV, ANCHOR_ARGUMENT)
+        )
+    );
+    Query query = createQuery(where, cV, pV, argV);
     Answer answer = query(query);
 
     if (logger.isDebugEnabled()) logger.debug("Retrieved all transitive constraints.");
@@ -975,13 +888,13 @@
    * to be created.  The constraintLinks and typeMap arguments are for constructing new
    * constraint objects.
    *
-   * @param constraints The set of constraint nodes to get the constraints for.  Whenever possible,
-   *             the constraints come from constraintMap.
-   * @param constraintLinks Linkage of join constraints to their arguments.  Used to create a new constraint.
+   * @param constraints The set of constraint nodes and their usage to get the constraints for.
+   *             Whenever possible, the constraints come from constraintMap.
+   * @param constraintLinks Linkage of join constraints to their arguments (and argument usage).  Used to create a new constraint.
    * @param typeMap Maps constraint nodes to their type.  Used to create a new constraint.
    * @throws KruleStructureException There was an error in the RDF data structure.
    */
-  private List<ConstraintExpression> getConstraints(Set<Node> constraints, Map<Node,Set<Node>> constraintLinks, Map<Node,URIReference> typeMap) throws KruleStructureException {
+  private List<ConstraintExpression> getConstraints(Set<Pair<Node,Node>> constraints, Map<Node,Set<Pair<Node,Node>>> constraintLinks, Map<Node,URIReference> typeMap) throws KruleStructureException {
     if (logger.isDebugEnabled()) logger.debug("converting nodes to constraint list: " + constraints);
 
     // build the return list
@@ -992,21 +905,27 @@
       return cList;
     }
     // go through the arguments
-    for (Node cNode: constraints) {
+    for (Pair<Node,Node> constraintUsage: constraints) {
+      Node usage = constraintUsage.first();  // one of: argument/minuend/subtrahend
+      Node cNode = constraintUsage.second();
       if (logger.isDebugEnabled()) logger.debug("converting: " + cNode);
       // get the constraint expression object
       ConstraintExpression constraintExpr = (ConstraintExpression)constraintMap.get(cNode);
       if (constraintExpr == null) {
         if (logger.isDebugEnabled()) logger.debug(cNode.toString() + " not yet mapped to constraint");
         // constraint expression object does not yet exist, get its arguments
-        Set<Node> constraintArgNodes = constraintLinks.get(cNode);
+        Set<Pair<Node,Node>> constraintArgNodes = constraintLinks.get(cNode);
         // build the constraint expression - get the arguments as a list of constraints
         List<ConstraintExpression> constraintArgs = getConstraints(constraintArgNodes, constraintLinks, typeMap);
         constraintExpr = newJoinConstraint((Node)typeMap.get(cNode), constraintArgs);
       }
       // add the constraint argument to the list
-      if (constraintExpr != null) cList.add(constraintExpr);
-      else logger.warn("Missing constraint expression. Ignoring.");
+      if (constraintExpr != null) {
+        if (usage.equals(MINUEND)) setList(cList, 0, constraintExpr);
+        else if (usage.equals(SUBTRAHEND)) setList(cList, 1, constraintExpr);
+        else if (usage.equals(ARGUMENT)) cList.add(constraintExpr);
+        else throw new KruleStructureException("Unknown argument type for " + cNode + ": " + usage);
+      } else logger.warn("Missing constraint expression. Ignoring.");
     }
     return cList;
   }
@@ -1028,8 +947,11 @@
       return new ConstraintConjunction(args);
     } else if (type.equals(CONSTRAINT_DISJUNCTION)) {
       return new ConstraintDisjunction(args);
+    } else if (type.equals(DIFFERENCE)) {
+      if (args.size() != 2) throw new KruleStructureException("Difference constraints require 2 arguments: args=" + args);
+      return new ConstraintDifference(args.get(0), args.get(1));
     }
-    throw new KruleStructureException("Unknown join constraint type (not AND/OR): " + type);
+    throw new KruleStructureException("Unknown join constraint type (not AND/OR/Minus): " + type);
   }
 
 
@@ -1085,29 +1007,64 @@
 
 
   /**
-   * Maps a node to another node, creating the entry if it does not exist yet.
+   * Maps a node to data about a nodee, creating the entry if it does not exist yet.
    *
    * @param map The mapping of nodes to tuples.
    * @param node1 The node to map.
-   * @param node2 The node to map it to.
+   * @param nodeData The node data to map it to.
    */
-  private static void addLink(Map<Node,Set<Node>> map, Node node1, Node node2) {
+  private static void addLink(Map<Node,Set<Pair<Node,Node>>> map, Node node1, Pair<Node,Node> nodeData) {
     // get the current set of properties
-    Set<Node> links = map.get(node1);
+    Set<Pair<Node,Node>> links = map.get(node1);
     // check that the set exists
     if (links == null) {
       // no, so create
-      links = new HashSet<Node>();
-      links.add(node2);
+      links = new HashSet<Pair<Node,Node>>();
+      links.add(nodeData);
       // add to the map
       map.put(node1, links);
     } else {
       // update the map to hold the new value
-      links.add(node2);
+      links.add(nodeData);
     }
   }
 
+
   /**
+   * Sets an element in a list, expanding the list if necessary.
+   * @param list The list to update.
+   * @param offset The offset to write to in the list.
+   * @param value The value to write to the list.
+   */
+  private static <T> void setList(List<T> list, int offset, T value) {
+    while (offset >= list.size()) list.add(null);
+    list.set(offset, value);
+  }
+
+
+  /**
+   * Utility method to create a query.
+   * @param constraintExpression The constraint expression making up the WHERE clause of the query.
+   * @param selection The variables to select in the query.
+   * @return The new query.
+   */
+  @SuppressWarnings("unchecked")
+  private Query createQuery(ConstraintExpression constraintExpression, Variable... selection) {
+    List<Variable> selectList = Arrays.asList(selection);
+    return new Query(
+        selectList,                                 // SELECT
+        ruleGraph,                                  // FROM
+        constraintExpression,                                      // WHERE
+        null,                                       // HAVING
+        (List<Order>)Collections.EMPTY_LIST,        // ORDER BY
+        null,                                       // LIMIT
+        0,                                          // OFFSET
+        UNCONSTRAINED                               // GIVEN
+    );
+  }
+
+
+  /**
    * Local wrapper for querying on an OperationContext. Since {@link OperationContext#doQuery(Query)}
    * throws an {@link Exception}, this is captured and wrapped in or cast to a {@link QueryException}.
    * @param q The query to execute.
@@ -1116,7 +1073,8 @@
    */
   private Answer query(Query q) throws QueryException {
     try {
-      return operationContext.doQuery(q);
+      if (operationContext != null) return operationContext.doQuery(q);
+      throw new IllegalStateException("No environment to query the database in");
     } catch (Exception e) {
       if (e instanceof QueryException) throw (QueryException)e;
       throw new QueryException("Unable to execute query", e);

Modified: trunk/src/jar/krule/java/org/mulgara/krule/QueryStruct.java
===================================================================
--- trunk/src/jar/krule/java/org/mulgara/krule/QueryStruct.java	2009-03-24 01:12:37 UTC (rev 1625)
+++ trunk/src/jar/krule/java/org/mulgara/krule/QueryStruct.java	2009-03-24 18:42:49 UTC (rev 1626)
@@ -41,6 +41,7 @@
 import org.mulgara.query.UnconstrainedAnswer;
 import org.mulgara.query.Variable;
 import org.mulgara.query.VariableFactory;
+import org.mulgara.query.rdf.Krule;
 import org.mulgara.query.rdf.LiteralImpl;
 import org.mulgara.query.rdf.URIReferenceImpl;
 
@@ -62,7 +63,7 @@
   private static Logger logger = Logger.getLogger(QueryStruct.class.getName());
 
   /** The selection list. */
-  private ConstraintElement[] select = new ConstraintElement[3];
+  private ConstraintElement[] select;
 
   /** List of elements which are variables, or ConstantValues. */
   private List<SelectElement> variables;
@@ -80,8 +81,8 @@
   /**
    * Constructor.  Converts string descriptions of the values and variables into the constraint elements.
    *
-   * @param vs The element nodes.
-   * @param types The types of the elements, defined in the krule namespace.
+   * @param valueSelection The element nodes.
+   * @param selTypes The types of the elements, defined in the krule namespace.
    * @param alias The aliases used in the query process.
    * @param uriReferences A map of all krule:ref_* objects to the appropriate {@link org.jrdf.graph.URIReference}s. 
    * @param varReferences A map of all krule:var_* objects to the appropriate name.
@@ -89,42 +90,57 @@
    *         or the references are not found in the references map.
    */
   public QueryStruct(
-      URIReference[] vs, URIReference[] types, Map<String,URI> alias,
+      List<URIReference> valueSelection, List<URIReference> selTypes, Map<String,URI> alias,
       Map<URIReference,URIReference> uriReferences, Map<URIReference,Variable> varReferences,
       Map<Node,Literal> litReferences
   ) {
 
-    if (vs.length != 3 && types.length != 3) {
+    if (valueSelection.size() <= 0 || selTypes.size() <= 0 || valueSelection.size() != selTypes.size()) {
       throw new IllegalArgumentException("Wrong number of elements for a rule query");
     }
 
+    URIReference[] vs = valueSelection.toArray(new URIReference[valueSelection.size()]);
+    URIReference[] types = selTypes.toArray(new URIReference[selTypes.size()]);
+
+    // If there is a non-multiple of 3 in the selection variables, then this is a check rule
+    // and we can only select variables in check rules
+    boolean varsOnly = vs.length % 3 != 0;
+
     VariableFactory variableFactory = new VariableFactoryImpl();
 
     // set up a list of variables
     variables = new ArrayList<SelectElement>();
+    select = new ConstraintElement[vs.length];
 
     // convert the parameters to usable objects
-    for (int i = 0; i < 3; i++) {
+    for (int i = 0; i < vs.length; i++) {
       URIReference element = vs[i];
       // check the type
-      if (types[i].equals(KruleLoader.URI_REF)) {
+      if (types[i].equals(Krule.URI_REF)) {
 
+        // check that this didn't have a non-multiple of 3 in the selection values
+        if (varsOnly) throw new IllegalArgumentException("Wrong number of elements for a rule query: " + vs.length);
+
         // get the referred value from the map
         select[i] = (URIReferenceImpl)uriReferences.get(element);
         // assume that literals do not have the "Value" type inferred
         variables.add(new ConstantValue(variableFactory.newVariable(), (URIReferenceImpl)select[i]));
 
-      } else if (types[i].equals(KruleLoader.VARIABLE)) {
+      } else if (types[i].equals(Krule.VARIABLE)) {
 
         // get the variable
         select[i] = (Variable)varReferences.get(element);
         variables.add((Variable)select[i]);
 
-      } else if (types[i].equals(KruleLoader.LITERAL)) {
+      } else if (types[i].equals(Krule.LITERAL)) {
         
-        if (i != 2) {
+        if (i % 3 != 2) {
           throw new IllegalArgumentException("Selection literal in illegal position in query");
         }
+
+        // check that this didn't have a non-multiple of 3 in the selection values
+        if (varsOnly) throw new IllegalArgumentException("Wrong number of elements for a rule query: " + vs.length);
+
         // get the literal
         select[i] = (LiteralImpl)litReferences.get(element);
         variables.add(new ConstantValue(variableFactory.newVariable(), (LiteralImpl)select[i]));
@@ -143,6 +159,15 @@
 
 
   /**
+   * Returns the number of elements to be returned from this query.
+   * @return The number of selection elements from the query.
+   */
+  public int elementCount() {
+    return select.length;
+  }
+
+
+  /**
    * Retrieve the element <em>n</em>.
    *
    * @param n The element number to retrieve.
@@ -150,7 +175,7 @@
    * @throws IndexOutOfBoundsException If n is larger than 3.
    */
   public ConstraintElement getElement(int n) {
-    assert n < 3;
+    assert n < select.length;
     return select[n];
   }
 

Modified: trunk/src/jar/krule/java/org/mulgara/krule/Rule.java
===================================================================
--- trunk/src/jar/krule/java/org/mulgara/krule/Rule.java	2009-03-24 01:12:37 UTC (rev 1625)
+++ trunk/src/jar/krule/java/org/mulgara/krule/Rule.java	2009-03-24 18:42:49 UTC (rev 1626)
@@ -51,13 +51,13 @@
   private static Logger logger = Logger.getLogger(Rule.class.getName());
 
   /** The name of this rule. */
-  private String name;
+  protected String name;
 
   /** The rules to be triggered when this rule generates statements.*/
   private Set<Rule> triggerSet;
 
   /** The query for this rule. This contains the information for the base model. */
-  private Query query;
+  protected Query query;
 
   /** The graph receiving the inferred data. */
   private long targetGraph = UNINITIALIZED;
@@ -66,7 +66,7 @@
   private long lastCount;
 
   /** The structure containing this rule */
-  private RuleStructure ruleStruct;
+  protected RuleStructure ruleStruct;
 
   // TODO: Change this to a map of constraints to longs
 
@@ -112,11 +112,15 @@
 
 
   /**
-   * Sets the query for this rule.
+   * Sets the query for this rule. Must be a valid query for inserting data, meaning that it
+   * returns a multiple of 3 elements.
    *
    * @param queryStruct The query which retrieves data for this rule.
+   * @throws KruleStructureException If the query does not return a multiple of 3 elements.
    */
-  public void setQueryStruct(QueryStruct queryStruct) {
+  public void setQueryStruct(QueryStruct queryStruct) throws KruleStructureException {
+    int e = queryStruct.elementCount();
+    if (e == 0 || e % 3 != 0) throw new KruleStructureException("Rule \"" + name + "\" attempting to generate the wrong number of elements (must be a multiple of 3): " + e);
     this.query = queryStruct.extractQuery();
   }
 

Modified: trunk/src/jar/query/java/org/mulgara/query/ConstraintConjunction.java
===================================================================
--- trunk/src/jar/query/java/org/mulgara/query/ConstraintConjunction.java	2009-03-24 01:12:37 UTC (rev 1625)
+++ trunk/src/jar/query/java/org/mulgara/query/ConstraintConjunction.java	2009-03-24 18:42:49 UTC (rev 1626)
@@ -93,6 +93,15 @@
 
 
   /**
+   * Build a conjunction with a {@link Collection} of several children constraint expressions
+   * @param elements a {@link Collection} of {@link ConstraintExpression}s.
+   */
+  public ConstraintConjunction(ConstraintExpression... elements) {
+    super(Arrays.asList(elements));
+  }
+
+
+  /**
    * Gets the Filtered attribute of the ConstraintConjunction object
    *
    * @return The Filtered value

Modified: trunk/src/jar/query/java/org/mulgara/query/ConstraintDisjunction.java
===================================================================
--- trunk/src/jar/query/java/org/mulgara/query/ConstraintDisjunction.java	2009-03-24 01:12:37 UTC (rev 1625)
+++ trunk/src/jar/query/java/org/mulgara/query/ConstraintDisjunction.java	2009-03-24 18:42:49 UTC (rev 1626)
@@ -82,6 +82,15 @@
   }
 
   /**
+   * Create a Disjunction (OR expression) between several constraint expressions.
+   *
+   * @param elements The expressions to perform a disjunction on.
+   */
+  public ConstraintDisjunction(ConstraintExpression... elements) {
+    super(Arrays.asList(elements));
+  }
+
+  /**
    * Gets the Name attribute of the ConstraintDisjunction object
    *
    * @return The Name value

Copied: trunk/src/jar/query/java/org/mulgara/query/rdf/Krule.java (from rev 1625, branches/consistency/src/jar/query/java/org/mulgara/query/rdf/Krule.java)
===================================================================
--- trunk/src/jar/query/java/org/mulgara/query/rdf/Krule.java	                        (rev 0)
+++ trunk/src/jar/query/java/org/mulgara/query/rdf/Krule.java	2009-03-24 18:42:49 UTC (rev 1626)
@@ -0,0 +1,213 @@
+/*
+ * Copyright 2008 Fedora Commons, Inc.
+ *
+ * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mulgara.query.rdf;
+
+import java.net.URI;
+
+/**
+ * URI constants for rules.
+ * 
+ * @created Mar 23, 2009
+ * @author Paul Gearon
+ * @copyright &copy; 2008 <a href="http://www.fedora-commons.org/">Fedora Commons</a>
+ */
+
+public class Krule {
+
+  /** URI for the Krule namespace. */
+  public static final String KRULE = "http://mulgara.org/owl/krule/#";
+
+  /** URI for a constraint subject. */
+  private static final String HAS_SUBJECT_STR = KRULE + "hasSubject";
+
+  /** URI for a constraint predicate. */
+  private static final String HAS_PREDICATE_STR = KRULE + "hasPredicate";
+
+  /** URI for a constraint object. */
+  private static final String HAS_OBJECT_STR = KRULE + "hasObject";
+
+  /** URI for a constraint model. */
+  private static final String HAS_GRAPH_STR = KRULE + "hasModel";
+
+  /** URI for a query property on rules. */
+  private static final String HAS_QUERY_STR = KRULE + "hasQuery";
+
+  /** URI for an axiom subject. */
+  private static final String AXIOM_SUBJECT_STR = KRULE + "subject";
+
+  /** URI for an axiom predicate. */
+  private static final String AXIOM_PREDICATE_STR = KRULE + "predicate";
+
+  /** URI for an axiom object. */
+  private static final String AXIOM_OBJECT_STR = KRULE + "object";
+
+  /** URI for rule triggering predicate. */
+  private static final String TRIGGERS_STR = KRULE + "triggers";
+
+  /** URI for selection variables in a query. */
+  private static final String SELECTION_VARS_STR = KRULE + "selectionVariables";
+
+  /** URI for constraints predicate in a query. */
+  private static final String HAS_WHERE_CLAUSE_STR = KRULE + "hasWhereClause";
+
+  /** URI for having constraints predicate in a query. */
+  private static final String HAS_HAVING_CLAUSE_STR = KRULE + "hasHavingClause";
+
+  /** URI for the argument property. */
+  private static final String ARGUMENT_STR = KRULE + "argument";
+
+  /** URI for the minuend property. */
+  private static final String MINUEND_STR = KRULE + "minuend";
+
+  /** URI for the subtrahend property. */
+  private static final String SUBTRAHEND_STR = KRULE + "subtrahend";
+
+  /** URI for the transitive constraint argument. */
+  private static final String TRANSITIVE_ARGUMENT_STR = KRULE + "transitiveArgument";
+
+  /** URI for the transitive constraint anchor argument. */
+  private static final String ANCHOR_ARGUMENT_STR = KRULE + "anchorArgument";
+
+  /** URI for the name argument. */
+  private static final String NAME_STR = KRULE + "name";
+
+  /** URI for the constraint conjunction type. */
+  private static final String CONSTRAINT_CONJUNCTION_STR = KRULE + "ConstraintConjunction";
+
+  /** URI for the constraint disjunction type. */
+  private static final String CONSTRAINT_DISJUNCTION_STR = KRULE + "ConstraintDisjunction";
+
+  /** URI for the simple constraint type. */
+  private static final String SIMPLE_CONSTRAINT_STR = KRULE + "SimpleConstraint";
+
+  /** URI for the transitive constraint type. */
+  private static final String TRANSITIVE_CONSTRAINT_STR = KRULE + "TransitiveConstraint";
+
+  /** URI for difference type. */
+  private static final String DIFFERENCE_STR = KRULE + "Difference";
+
+  /** URI for the Value type. */
+  private static final String URI_REF_STR = KRULE + "URIReference";
+
+  /** URI for the Variable type. */
+  private static final String VARIABLE_STR = KRULE + "Variable";
+
+  /** URI for the Variable type. */
+  private static final String LITERAL_STR = KRULE + "Literal";
+
+  /** URI for axiom type. */
+  private static final String AXIOM_STR = KRULE + "Axiom";
+
+  /** URI for rule type. */
+  private static final String RULE_STR = KRULE + "Rule";
+
+  /** URI for consistency check type. */
+  private static final String CHECK_STR = KRULE + "ConsistencyCheck";
+
+  /** URI for query type. */
+  private static final String QUERY_STR = KRULE + "Query";
+
+  /** RDF reference for constraint subject. */
+  public static final URIReferenceImpl HAS_SUBJECT = new URIReferenceImpl(URI.create(HAS_SUBJECT_STR));
+
+  /** RDF reference for constraint predicate. */
+  public static final URIReferenceImpl HAS_PREDICATE = new URIReferenceImpl(URI.create(HAS_PREDICATE_STR));
+
+  /** RDF reference for constraint object. */
+  public static final URIReferenceImpl HAS_OBJECT = new URIReferenceImpl(URI.create(HAS_OBJECT_STR));
+
+  /** RDF reference for constraint model. */
+  public static final URIReferenceImpl HAS_GRAPH = new URIReferenceImpl(URI.create(HAS_GRAPH_STR));
+
+  /** RDF reference for query property on rules. */
+  public static final URIReferenceImpl HAS_QUERY = new URIReferenceImpl(URI.create(HAS_QUERY_STR));
+
+  /** RDF reference for axiom subject. */
+  public static final URIReferenceImpl AXIOM_SUBJECT = new URIReferenceImpl(URI.create(AXIOM_SUBJECT_STR));
+
+  /** RDF reference for axiom predicate. */
+  public static final URIReferenceImpl AXIOM_PREDICATE = new URIReferenceImpl(URI.create(AXIOM_PREDICATE_STR));
+
+  /** RDF reference for axiom object. */
+  public static final URIReferenceImpl AXIOM_OBJECT = new URIReferenceImpl(URI.create(AXIOM_OBJECT_STR));
+
+  /** RDF reference for rule triggering predicate. */
+  public static final URIReferenceImpl TRIGGERS = new URIReferenceImpl(URI.create(TRIGGERS_STR));
+
+  /** RDF reference for selection variables predicate. */
+  public static final URIReferenceImpl SELECTION_VARS = new URIReferenceImpl(URI.create(SELECTION_VARS_STR));
+
+  /** RDF reference for hasWhereClause predicate. */
+  public static final URIReferenceImpl HAS_WHERE_CLAUSE = new URIReferenceImpl(URI.create(HAS_WHERE_CLAUSE_STR));
+
+  /** RDF reference for hasHavingClause predicate. */
+  public static final URIReferenceImpl HAS_HAVING_CLAUSE = new URIReferenceImpl(URI.create(HAS_HAVING_CLAUSE_STR));
+
+  /** RDF reference for the argument property. */
+  public static final URIReferenceImpl ARGUMENT = new URIReferenceImpl(URI.create(ARGUMENT_STR));
+
+  /** RDF reference for the minuend property. */
+  public static final URIReferenceImpl MINUEND = new URIReferenceImpl(URI.create(MINUEND_STR));
+
+  /** RDF reference for the subtrahend property. */
+  public static final URIReferenceImpl SUBTRAHEND = new URIReferenceImpl(URI.create(SUBTRAHEND_STR));
+
+  /** RDF reference for the transitive constraint argument. */
+  public static final URIReferenceImpl TRANSITIVE_ARGUMENT = new URIReferenceImpl(URI.create(TRANSITIVE_ARGUMENT_STR));
+
+  /** RDF reference for the transitive constraint anchor argument. */
+  public static final URIReferenceImpl ANCHOR_ARGUMENT = new URIReferenceImpl(URI.create(ANCHOR_ARGUMENT_STR));
+
+  /** RDF reference for the name argument. */
+  public static final URIReferenceImpl NAME = new URIReferenceImpl(URI.create(NAME_STR));
+
+  /** RDF reference for constraint conjunction class. */
+  public static final URIReferenceImpl CONSTRAINT_CONJUNCTION = new URIReferenceImpl(URI.create(CONSTRAINT_CONJUNCTION_STR));
+
+  /** RDF reference for constraint disjunction class. */
+  public static final URIReferenceImpl CONSTRAINT_DISJUNCTION = new URIReferenceImpl(URI.create(CONSTRAINT_DISJUNCTION_STR));
+
+  /** RDF reference for the simple constraint type. */
+  public static final URIReferenceImpl SIMPLE_CONSTRAINT = new URIReferenceImpl(URI.create(SIMPLE_CONSTRAINT_STR));
+
+  /** RDF reference for the transitive constraint type. */
+  public static final URIReferenceImpl TRANSITIVE_CONSTRAINT = new URIReferenceImpl(URI.create(TRANSITIVE_CONSTRAINT_STR));
+
+  /** RDF reference for the Difference type. */
+  public static final URIReferenceImpl DIFFERENCE = new URIReferenceImpl(URI.create(DIFFERENCE_STR));
+
+  /** RDF reference for the Value type. */
+  public static final URIReferenceImpl URI_REF = new URIReferenceImpl(URI.create(URI_REF_STR));
+
+  /** RDF reference for the Variable type. */
+  public static final URIReferenceImpl VARIABLE = new URIReferenceImpl(URI.create(VARIABLE_STR));
+
+  /** RDF reference for the Literal type. */
+  public static final URIReferenceImpl LITERAL = new URIReferenceImpl(URI.create(LITERAL_STR));
+
+  /** RDF reference for the Axiom type. */
+  public static final URIReferenceImpl AXIOM = new URIReferenceImpl(URI.create(AXIOM_STR));
+
+  /** RDF reference for the Rule type. */
+  public static final URIReferenceImpl RULE = new URIReferenceImpl(URI.create(RULE_STR));
+
+  /** RDF reference for the Consistency Check type. */
+  public static final URIReferenceImpl CHECK = new URIReferenceImpl(URI.create(CHECK_STR));
+
+  /** RDF reference for the Query type. */
+  public static final URIReferenceImpl QUERY = new URIReferenceImpl(URI.create(QUERY_STR));
+
+}

Modified: trunk/src/jar/query/java/org/mulgara/query/rdf/Mulgara.java
===================================================================
--- trunk/src/jar/query/java/org/mulgara/query/rdf/Mulgara.java	2009-03-24 01:12:37 UTC (rev 1625)
+++ trunk/src/jar/query/java/org/mulgara/query/rdf/Mulgara.java	2009-03-24 18:42:49 UTC (rev 1626)
@@ -27,6 +27,8 @@
 
 package org.mulgara.query.rdf;
 
+import java.net.URI;
+
 /**
  * General constants.
  *
@@ -57,4 +59,14 @@
 
   /** An identifier for a XSD resolver graph */
   public final static String XSD_GRAPH = "sys:xsd";
+
+  /** The URI for prefix graph types. */
+  public final static URI PREFIX_GRAPH_TYPE_URI = URI.create(NAMESPACE + "PrefixGraph");
+
+  /** The URI for prefixes. */
+  public final static URI PREFIX_URI = URI.create(NAMESPACE + "prefix");
+
+  /** The URI for prefixes. */
+  public final static URI IS_URI = URI.create(NAMESPACE + "is");
+
 }

Modified: trunk/src/jar/resolver/build.xml
===================================================================
--- trunk/src/jar/resolver/build.xml	2009-03-24 01:12:37 UTC (rev 1625)
+++ trunk/src/jar/resolver/build.xml	2009-03-24 18:42:49 UTC (rev 1626)
@@ -33,6 +33,7 @@
     <fileset file="${resolver-url.dist.dir}/${resolver-url.jar}"/>
     <fileset file="${content-rdfxml.dist.dir}/${content-rdfxml.jar}"/>
     <fileset file="${content-n3.dist.dir}/${content-n3.jar}"/>
+    <fileset file="${content-rlog.dist.dir}/${content-rlog.jar}"/>
     <fileset file="${driver.dist.dir}/${driver.jar}"/>
   </path>
 
@@ -82,7 +83,7 @@
   <target name="resolver-compile"
           depends="-resolver-prepare, resolver-spi-jar, rules-jar,
                    resolver-http-jar, resolver-file-jar, resolver-url-jar,
-                   resolver-jar-jar, content-rdfxml-jar, content-n3-jar,
+                   resolver-jar-jar, content-rdfxml-jar, content-n3-jar, content-rlog-jar,
                    client-jrdf-jar, jrdf-jar, resolver-xsd-jar, driver-jar"
           description="Compiles all resolver related files included generated
                        source code">

Modified: trunk/src/jar/resolver/java/org/mulgara/resolver/BuildRulesOperation.java
===================================================================
--- trunk/src/jar/resolver/java/org/mulgara/resolver/BuildRulesOperation.java	2009-03-24 01:12:37 UTC (rev 1625)
+++ trunk/src/jar/resolver/java/org/mulgara/resolver/BuildRulesOperation.java	2009-03-24 18:42:49 UTC (rev 1626)
@@ -81,7 +81,7 @@
     if (ruleLoader == null) throw new org.mulgara.rules.InitializerException("No rule loader available");
   
     // read in the rules
-    Rules rules =  ruleLoader.readRules(operationContext, metadata.getSystemModelURI());
+    Rules rules =  ruleLoader.readRules(operationContext);
     result = new RulesRefImpl(rules);
   }
 

Modified: trunk/src/jar/resolver/java/org/mulgara/resolver/ExportOperation.java
===================================================================
--- trunk/src/jar/resolver/java/org/mulgara/resolver/ExportOperation.java	2009-03-24 01:12:37 UTC (rev 1625)
+++ trunk/src/jar/resolver/java/org/mulgara/resolver/ExportOperation.java	2009-03-24 18:42:49 UTC (rev 1626)
@@ -17,6 +17,7 @@
 import java.util.Map;
 
 import org.mulgara.content.rdfxml.writer.RDFXMLWriter;
+import org.mulgara.content.rlog.RlogStructure;
 import org.mulgara.content.n3.N3Writer;
 import org.mulgara.query.Constraint;
 import org.mulgara.query.ConstraintImpl;
@@ -108,6 +109,10 @@
           if (path != null && (path.endsWith(".n3") || path.endsWith(".nt") || path.endsWith(".ttl"))) {
             N3Writer n3Writer = new N3Writer();
             n3Writer.write(graphStatements, systemResolver, writer);
+          } else if (path != null && (path.endsWith(".rl") || path.endsWith(".dl") || path.endsWith(".rlog"))) {
+            RlogStructure struct = new RlogStructure(systemResolver);
+            struct.load(graphStatements);
+            struct.write(writer);
           } else {
             RDFXMLWriter rdfWriter = new RDFXMLWriter();
             rdfWriter.write(graphStatements, systemResolver, writer, prefixes);

Modified: trunk/src/jar/resolver-prefix/java/org/mulgara/resolver/prefix/PrefixResolverFactory.java
===================================================================
--- trunk/src/jar/resolver-prefix/java/org/mulgara/resolver/prefix/PrefixResolverFactory.java	2009-03-24 01:12:37 UTC (rev 1625)
+++ trunk/src/jar/resolver-prefix/java/org/mulgara/resolver/prefix/PrefixResolverFactory.java	2009-03-24 18:42:49 UTC (rev 1626)
@@ -38,15 +38,9 @@
   /** Logger. */
   private static Logger logger = Logger.getLogger(PrefixResolverFactory.class.getName());
 
-  /** The URI for the graphType.  */
-  private static final URI graphTypeURI = URI.create(Mulgara.NAMESPACE + "PrefixGraph");
-
   /** The URI for the default graph. */
   private static final URI DEFAULT_GRAPH = URI.create(Mulgara.PREFIX_GRAPH);
 
-  /** The URI for prefixes. */
-  private static final URI mulgaraPrefixURI = URI.create(Mulgara.NAMESPACE + "prefix");
-
   /** The preallocated local node representing the prefix predicate. */
   private long mulgaraPrefix;
 
@@ -66,7 +60,7 @@
     }
 
     // intialize the fields
-    mulgaraPrefix = initializer.preallocate(new URIReferenceImpl(mulgaraPrefixURI));
+    mulgaraPrefix = initializer.preallocate(new URIReferenceImpl(Mulgara.PREFIX_URI));
 
     // no need to claim the types supported by this resolver, as this is detected in the default graphs
   }
@@ -116,14 +110,14 @@
       boolean canWrite, ResolverSession resolverSession, Resolver systemResolver
   ) throws ResolverFactoryException {
     if (logger.isDebugEnabled()) logger.debug("Creating new Prefix resolver");
-    return new PrefixResolver(resolverSession, systemResolver, mulgaraPrefix, graphTypeURI);
+    return new PrefixResolver(resolverSession, systemResolver, mulgaraPrefix, Mulgara.PREFIX_GRAPH_TYPE_URI);
   }
 
   /**
    * @return The default graph for this resolver.
    */
   public Graph[] getDefaultGraphs() {
-    return new Graph[] { new Graph(DEFAULT_GRAPH, graphTypeURI) };
+    return new Graph[] { new Graph(DEFAULT_GRAPH, Mulgara.PREFIX_GRAPH_TYPE_URI) };
   }
   
   /**

Modified: trunk/src/jar/resolver-relational/java/org/mulgara/resolver/relational/RelationalResolverUnitTest.java
===================================================================
--- trunk/src/jar/resolver-relational/java/org/mulgara/resolver/relational/RelationalResolverUnitTest.java	2009-03-24 01:12:37 UTC (rev 1625)
+++ trunk/src/jar/resolver-relational/java/org/mulgara/resolver/relational/RelationalResolverUnitTest.java	2009-03-24 18:42:49 UTC (rev 1626)
@@ -43,11 +43,8 @@
 
 // Java 2 standard packages
 import java.io.File;
-import java.io.PrintWriter;
-import java.io.StringWriter;
 import java.net.URI;
 import java.net.URISyntaxException;
-import java.net.InetAddress;
 import java.util.*;
 import java.sql.*;
 
@@ -58,14 +55,9 @@
 
 // Locally written packages
 import org.mulgara.query.*;
-import org.mulgara.query.rdf.Mulgara;
 import org.mulgara.query.rdf.URIReferenceImpl;
 import org.mulgara.query.rdf.LiteralImpl;
 import org.mulgara.server.Session;
-import org.mulgara.store.StoreException;
-import org.mulgara.store.nodepool.NodePool;
-import org.mulgara.store.stringpool.StringPool;
-import org.mulgara.store.tuples.AbstractTuples;
 import org.mulgara.util.FileUtil;
 import org.mulgara.util.TempDir;
 
@@ -109,9 +101,12 @@
     }
   }
 
+  /** create the temporary directory */
+  @SuppressWarnings("unused")
   private static final File tmpDir = TempDir.getTempDir();
 
   /** Logger.  */
+  @SuppressWarnings("unused")
   private static Logger logger =
     Logger.getLogger(RelationalResolverUnitTest.class.getName());
 
@@ -225,9 +220,6 @@
       String tempResolverFactoryClassName =
         "org.mulgara.resolver.memory.MemoryResolverFactory";
 
-      String ruleLoaderFactoryClassName =
-        "org.mulgara.rules.RuleLoaderFactory";
-
       // Create a database which keeps its system models on the Java heap
       database = new Database(
                    databaseURI,
@@ -380,7 +372,7 @@
   public void testBasicClassQuery() throws Exception {
     Session session = database.newSession();
     try {
-      List selectList = Collections.singletonList(new Variable("s"));
+      List<Variable> selectList = Collections.singletonList(new Variable("s"));
 
 
       Answer answer = session.query(new Query(
@@ -414,8 +406,8 @@
   public void testBasicPropertyQuery() throws Exception {
     Session session = database.newSession();
     try {
-      List selectList = Arrays.asList(new Variable[] { new Variable("s"), new Variable("fn") });
-      List orderList = Arrays.asList(new Order[] {
+      List<Variable> selectList = Arrays.asList(new Variable[] { new Variable("s"), new Variable("fn") });
+      List<Order> orderList = Arrays.asList(new Order[] {
           new Order(new Variable("s"), true), 
           new Order(new Variable("fn"), true)
         });
@@ -458,8 +450,8 @@
   public void testBoundPropertyQuery() throws Exception {
     Session session = database.newSession();
     try {
-      List selectList = Arrays.asList(new Variable[] { new Variable("s") });
-      List orderList = Arrays.asList(new Order[] {
+      List<Variable> selectList = Arrays.asList(new Variable[] { new Variable("s") });
+      List<Order> orderList = Arrays.asList(new Order[] {
           new Order(new Variable("s"), true), 
         });
 
@@ -499,8 +491,8 @@
   public void testBoundSubjectQuery() throws Exception {
     Session session = database.newSession();
     try {
-      List selectList = Arrays.asList(new Variable[] { new Variable("o") });
-      List orderList = Arrays.asList(new Order[] {
+      List<Variable> selectList = Arrays.asList(new Variable[] { new Variable("o") });
+      List<Order> orderList = Arrays.asList(new Order[] {
           new Order(new Variable("o"), true), 
         });
 
@@ -540,8 +532,8 @@
   public void testConjPropertyQuery() throws Exception {
     Session session = database.newSession();
     try {
-      List selectList = Arrays.asList(new Variable[] { new Variable("s"), new Variable("fn") });
-      List orderList = Arrays.asList(new Order[] {
+      List<Variable> selectList = Arrays.asList(new Variable[] { new Variable("s"), new Variable("fn") });
+      List<Order> orderList = Arrays.asList(new Order[] {
           new Order(new Variable("s"), true), 
           new Order(new Variable("fn"), true)
         });
@@ -590,7 +582,7 @@
   public void testURIPatternQuery() throws Exception {
     Session session = database.newSession();
     try {
-      List selectList = Collections.singletonList(new Variable("s"));
+      List<Variable> selectList = Collections.singletonList(new Variable("s"));
 
 
       Answer answer = session.query(new Query(
@@ -624,8 +616,8 @@
   public void testURIPatternPropertyQuery() throws Exception {
     Session session = database.newSession();
     try {
-      List selectList = Arrays.asList(new Variable[] { new Variable("s"), new Variable("loc") });
-      List orderList = Arrays.asList(new Order[] {
+      List<Variable> selectList = Arrays.asList(new Variable[] { new Variable("s"), new Variable("loc") });
+      List<Order> orderList = Arrays.asList(new Order[] {
           new Order(new Variable("s"), true), 
           new Order(new Variable("loc"), true)
         });
@@ -668,8 +660,8 @@
   public void testBasicPredicateQuery() throws Exception {
     Session session = database.newSession();
     try {
-      List selectList = Arrays.asList(new Variable[] { new Variable("s"), new Variable("p") });
-      List orderList = Arrays.asList(new Order[] {
+      List<Variable> selectList = Arrays.asList(new Variable[] { new Variable("s"), new Variable("p") });
+      List<Order> orderList = Arrays.asList(new Order[] {
           new Order(new Variable("s"), true), 
           new Order(new Variable("p"), true)
         });
@@ -709,8 +701,8 @@
   public void testBasicPredicateObjectQuery() throws Exception {
     Session session = database.newSession();
     try {
-      List selectList = Arrays.asList(new Variable[] { new Variable("s"), new Variable("p"), new Variable("o") });
-      List orderList = Arrays.asList(new Order[] {
+      List<Variable> selectList = Arrays.asList(new Variable[] { new Variable("s"), new Variable("p"), new Variable("o") });
+      List<Order> orderList = Arrays.asList(new Order[] {
           new Order(new Variable("s"), true), 
           new Order(new Variable("p"), true),
           new Order(new Variable("o"), true)
@@ -788,8 +780,8 @@
   public void testCompoundPredicateVoidQuery() throws Exception {
     Session session = database.newSession();
     try {
-      List selectList = Arrays.asList(new Variable[] { new Variable("s"), new Variable("p"), new Variable("q") });
-      List orderList = Arrays.asList(new Order[] {
+      List<Variable> selectList = Arrays.asList(new Variable[] { new Variable("s"), new Variable("p"), new Variable("q") });
+      List<Order> orderList = Arrays.asList(new Order[] {
           new Order(new Variable("s"), true), 
           new Order(new Variable("p"), true),
           new Order(new Variable("q"), true)
@@ -833,8 +825,8 @@
   public void testCompoundPredicateQuery() throws Exception {
     Session session = database.newSession();
     try {
-      List selectList = Arrays.asList(new Variable[] { new Variable("s"), new Variable("p"), new Variable("q") });
-      List orderList = Arrays.asList(new Order[] {
+      List<Variable> selectList = Arrays.asList(new Variable[] { new Variable("s"), new Variable("p"), new Variable("q") });
+      List<Order> orderList = Arrays.asList(new Order[] {
           new Order(new Variable("s"), true), 
           new Order(new Variable("p"), true),
           new Order(new Variable("q"), true)
@@ -882,8 +874,8 @@
   public void testCompoundPredicateObjectQuery() throws Exception {
     Session session = database.newSession();
     try {
-      List selectList = Arrays.asList(new Variable[] { new Variable("s"), new Variable("p"), new Variable("o") });
-      List orderList = Arrays.asList(new Order[] {
+      List<Variable> selectList = Arrays.asList(new Variable[] { new Variable("s"), new Variable("p"), new Variable("o") });
+      List<Order> orderList = Arrays.asList(new Order[] {
           new Order(new Variable("s"), true), 
           new Order(new Variable("p"), true),
           new Order(new Variable("o"), true)
@@ -947,8 +939,8 @@
   public void testBasicPatternPropertyQuery() throws Exception {
     Session session = database.newSession();
     try {
-      List selectList = Arrays.asList(new Variable[] { new Variable("s"), new Variable("title") });
-      List orderList = Arrays.asList(new Order[] {
+      List<Variable> selectList = Arrays.asList(new Variable[] { new Variable("s"), new Variable("title") });
+      List<Order> orderList = Arrays.asList(new Order[] {
           new Order(new Variable("s"), true), 
           new Order(new Variable("title"), true)
         });
@@ -994,8 +986,8 @@
   public void testBoundPatternPropertyQuery() throws Exception {
     Session session = database.newSession();
     try {
-      List selectList = Arrays.asList(new Variable[] { new Variable("s") });
-      List orderList = Arrays.asList(new Order[] {
+      List<Variable> selectList = Arrays.asList(new Variable[] { new Variable("s") });
+      List<Order> orderList = Arrays.asList(new Order[] {
           new Order(new Variable("s"), true), 
         });
 
@@ -1035,8 +1027,8 @@
   public void testBoundPatternSubjectQuery() throws Exception {
     Session session = database.newSession();
     try {
-      List selectList = Arrays.asList(new Variable[] { new Variable("o") });
-      List orderList = Arrays.asList(new Order[] {
+      List<Variable> selectList = Arrays.asList(new Variable[] { new Variable("o") });
+      List<Order> orderList = Arrays.asList(new Order[] {
           new Order(new Variable("o"), true), 
         });
 
@@ -1077,8 +1069,8 @@
   public void testBasicReferWithJoinQuery() throws Exception {
     Session session = database.newSession();
     try {
-      List selectList = Arrays.asList(new Variable[] { new Variable("name"), new Variable("abstract") });
-      List orderList = Arrays.asList(new Order[] {
+      List<Variable> selectList = Arrays.asList(new Variable[] { new Variable("name"), new Variable("abstract") });
+      List<Order> orderList = Arrays.asList(new Order[] {
           new Order(new Variable("name"), true), 
           new Order(new Variable("abstract"), true), 
         });
@@ -1137,12 +1129,12 @@
   public void testBasicReferWithJoinQueryAndPropPattern() throws Exception {
     Session session = database.newSession();
     try {
-      List selectList = Arrays.asList(new Variable[] {
+      List<Variable> selectList = Arrays.asList(new Variable[] {
           new Variable("name"),
           new Variable("abstract"),
           new Variable("title"),
         });
-      List orderList = Arrays.asList(new Order[] {
+      List<Order> orderList = Arrays.asList(new Order[] {
           new Order(new Variable("name"), true), 
           new Order(new Variable("abstract"), true), 
           new Order(new Variable("title"), true),
@@ -1209,11 +1201,11 @@
   public void testBasicReferWithPropPattern() throws Exception {
     Session session = database.newSession();
     try {
-      List selectList = Arrays.asList(new Variable[] {
+      List<Variable> selectList = Arrays.asList(new Variable[] {
           new Variable("abstract"),
           new Variable("title"),
         });
-      List orderList = Arrays.asList(new Order[] {
+      List<Order> orderList = Arrays.asList(new Order[] {
           new Order(new Variable("abstract"), true), 
           new Order(new Variable("title"), true),
         });
@@ -1264,8 +1256,8 @@
   public void testObjectReferWithMNJoin() throws Exception {
     Session session = database.newSession();
     try {
-      List selectList = Arrays.asList(new Variable[] { new Variable("title"), new Variable("author") });
-      List orderList = Arrays.asList(new Order[] {
+      List<Variable> selectList = Arrays.asList(new Variable[] { new Variable("title"), new Variable("author") });
+      List<Order> orderList = Arrays.asList(new Order[] {
           new Order(new Variable("title"), true), 
           new Order(new Variable("author"), true), 
         });
@@ -1327,8 +1319,8 @@
   public void testObjectReferWithPK() throws Exception {
     Session session = database.newSession();
     try {
-      List selectList = Arrays.asList(new Variable[] { new Variable("author"), new Variable("paper") });
-      List orderList = Arrays.asList(new Order[] {
+      List<Variable> selectList = Arrays.asList(new Variable[] { new Variable("author"), new Variable("paper") });
+      List<Order> orderList = Arrays.asList(new Order[] {
           new Order(new Variable("author"), true), 
           new Order(new Variable("paper"), true), 
         });
@@ -1382,8 +1374,8 @@
   public void testBasicQueryWithAdditionalProperty() throws Exception {
     Session session = database.newSession();
     try {
-      List selectList = Arrays.asList(new Variable[] { new Variable("title"), new Variable("seeAlso") });
-      List orderList = Arrays.asList(new Order[] {
+      List<Variable> selectList = Arrays.asList(new Variable[] { new Variable("title"), new Variable("seeAlso") });
+      List<Order> orderList = Arrays.asList(new Order[] {
           new Order(new Variable("title"), true), 
           new Order(new Variable("seeAlso"), true), 
         });
@@ -1451,8 +1443,8 @@
   public void testDualDatabaseQuery() throws Exception {
     Session session = database.newSession();
     try {
-      List selectList = Arrays.asList(new Variable[] { new Variable("s"), new Variable("loc") });
-      List orderList = Arrays.asList(new Order[] {
+      List<Variable> selectList = Arrays.asList(new Variable[] { new Variable("s"), new Variable("loc") });
+      List<Order> orderList = Arrays.asList(new Order[] {
           new Order(new Variable("s"), true), 
           new Order(new Variable("loc"), true)
         });

Modified: trunk/src/jar/rules/java/org/mulgara/rules/DummyRuleLoader.java
===================================================================
--- trunk/src/jar/rules/java/org/mulgara/rules/DummyRuleLoader.java	2009-03-24 01:12:37 UTC (rev 1625)
+++ trunk/src/jar/rules/java/org/mulgara/rules/DummyRuleLoader.java	2009-03-24 18:42:49 UTC (rev 1626)
@@ -17,7 +17,6 @@
 package org.mulgara.rules;
 
 import java.net.URI;
-import java.rmi.RemoteException;
 
 /**
  * A dummy implementation of the rule loader.
@@ -36,7 +35,7 @@
    *
    * @return Nothing.
    */
-  public Rules readRules(Object session, URI systemModel) throws InitializerException {
+  public Rules readRules(Object session) throws InitializerException {
     throw new InitializerException("No rule loader available.");
   }
 

Modified: trunk/src/jar/rules/java/org/mulgara/rules/RuleLoader.java
===================================================================
--- trunk/src/jar/rules/java/org/mulgara/rules/RuleLoader.java	2009-03-24 01:12:37 UTC (rev 1625)
+++ trunk/src/jar/rules/java/org/mulgara/rules/RuleLoader.java	2009-03-24 18:42:49 UTC (rev 1626)
@@ -16,7 +16,6 @@
 
 package org.mulgara.rules;
 
-import java.net.URI;
 import java.rmi.RemoteException;
 
 /**
@@ -36,6 +35,6 @@
    *
    * @return A {@link org.mulgara.krule.RuleStructure} containing all the loaded rules.
    */
-  public Rules readRules(Object session, URI systemModel) throws InitializerException, RemoteException;
+  public Rules readRules(Object session) throws InitializerException, RemoteException;
 
 }

Modified: trunk/src/jar/rules/java/org/mulgara/rules/RuleLoaderFactory.java
===================================================================
--- trunk/src/jar/rules/java/org/mulgara/rules/RuleLoaderFactory.java	2009-03-24 01:12:37 UTC (rev 1625)
+++ trunk/src/jar/rules/java/org/mulgara/rules/RuleLoaderFactory.java	2009-03-24 18:42:49 UTC (rev 1626)
@@ -47,20 +47,15 @@
    * @param target  the destination for rule consequences.
    * @return the constructed {@link RuleLoader}
    */
-  public static RuleLoader newRuleLoader(String className, URI source, URI base, URI target)
-    throws InitializerException
-  {
-    if (logger.isDebugEnabled()) {
-      logger.debug("Creating rule loader " + className);
-    }
+  public static RuleLoader newRuleLoader(String className, URI source, URI base, URI target) throws InitializerException {
 
+    if (logger.isDebugEnabled()) logger.debug("Creating rule loader " + className);
+
     // Validate parameters
-    if (className == null) {
-      throw new IllegalArgumentException("Null 'className' parameter");
-    }
+    if (className == null) throw new IllegalArgumentException("Null 'className' parameter");
 
     try {
-      Class ruleLoaderClass = Class.forName(className);
+      Class<?> ruleLoaderClass = Class.forName(className);
 
       // Validate that the candidate supports the RuleLoader interface
       if (!RuleLoader.class.isAssignableFrom(ruleLoaderClass)) {
@@ -80,8 +75,8 @@
         newInstanceMethod.invoke(null, new Object[] { source, base, target });
 
       return ruleLoader;
-    }
-    catch (Exception e) {
+
+    } catch (Exception e) {
       logger.warn("Error generating rule loader factory", e);
       throw new InitializerException("Unable to add rule loader factory", e);
     }

Modified: trunk/src/jar/rules/java/org/mulgara/rules/RulesRef.java
===================================================================
--- trunk/src/jar/rules/java/org/mulgara/rules/RulesRef.java	2009-03-24 01:12:37 UTC (rev 1625)
+++ trunk/src/jar/rules/java/org/mulgara/rules/RulesRef.java	2009-03-24 18:42:49 UTC (rev 1626)
@@ -16,8 +16,6 @@
 
 package org.mulgara.rules;
 
-import java.net.URI;
-import java.io.Serializable;
 import java.rmi.Remote;
 import java.rmi.RemoteException;
 

Modified: trunk/src/jar/store-stringpool-xa11/java/org/mulgara/store/stringpool/xa11/XA11StringPoolLoadTest.java
===================================================================
--- trunk/src/jar/store-stringpool-xa11/java/org/mulgara/store/stringpool/xa11/XA11StringPoolLoadTest.java	2009-03-24 01:12:37 UTC (rev 1625)
+++ trunk/src/jar/store-stringpool-xa11/java/org/mulgara/store/stringpool/xa11/XA11StringPoolLoadTest.java	2009-03-24 18:42:49 UTC (rev 1626)
@@ -29,7 +29,7 @@
 import org.mulgara.util.TempDir;
 
 /**
- * Test case for {@link XAStringPoolImpl}.
+ * Test case for {@link XA11StringPoolImpl}.
  *
  * @created 2008-08-20
  *

Modified: trunk/src/jar/util/java/org/mulgara/util/functional/C.java
===================================================================
--- trunk/src/jar/util/java/org/mulgara/util/functional/C.java	2009-03-24 01:12:37 UTC (rev 1625)
+++ trunk/src/jar/util/java/org/mulgara/util/functional/C.java	2009-03-24 18:42:49 UTC (rev 1626)
@@ -20,6 +20,7 @@
 import java.util.Collection;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.ListIterator;
 import java.util.NoSuchElementException;
 
 /**
@@ -48,7 +49,7 @@
    * @return A list whose elements are the result of applying op to each element of args.
    * @throws E An exception that may be thrown from the {@link Fn1E#fn(Object)} method.
    */
-  public static <T1,T2,E extends Exception> List<T2> map(Collection<T1> args, Fn1E<T1,T2,E> op) throws E {
+  public static final <T1,T2,E extends Exception> List<T2> map(Collection<T1> args, Fn1E<T1,T2,E> op) throws E {
     List<T2> result = new LinkedList<T2>();
     for (T1 a: args) result.add(op.fn(a));
     return result;
@@ -57,7 +58,7 @@
   /**
    * The same method as {@link #map(Collection, Fn1E)} for arrays.
    */
-  public static <T1,T2,E extends Exception> List<T2> map(T1[] args, Fn1E<T1,T2,E> op) throws E {
+  public static final <T1,T2,E extends Exception> List<T2> map(T1[] args, Fn1E<T1,T2,E> op) throws E {
     List<T2> result = new ArrayList<T2>(args.length);
     for (T1 a: args) result.add(op.fn(a));
     return result;
@@ -76,14 +77,14 @@
    * @param op The operation to apply to the elements of the input list.
    * @return A list whose elements are the result of applying op to each element of args.
    */
-  public static <T1,T2> List<T2> map(Collection<T1> args, Fn1<T1,T2> op) {
+  public static final <T1,T2> List<T2> map(Collection<T1> args, Fn1<T1,T2> op) {
     return map(args, (Fn1E<T1,T2,RuntimeException>)op);
   }
 
   /**
    * The same method as {@link #map(Collection, Fn1)} for arrays.
    */
-  public static <T1,T2> List<T2> map(T1[] args, Fn1<T1,T2> op) {
+  public static final <T1,T2> List<T2> map(T1[] args, Fn1<T1,T2> op) {
     List<T2> result = new ArrayList<T2>(args.length);
     for (T1 a: args) result.add(op.fn(a));
     return result;
@@ -100,7 +101,7 @@
    * @return The first element in the list.
    * @throws NoSuchElementException If the list is empty.
    */
-  public static <T1> T1 head(LinkedList<T1> arg) throws NoSuchElementException {
+  public static final <T1> T1 head(LinkedList<T1> arg) throws NoSuchElementException {
     return arg.getFirst();
   }
 
@@ -111,7 +112,7 @@
    * @return The first element in the list.
    * @throws NoSuchElementException If the list is empty.
    */
-  public static <T1> T1 head(List<T1> arg) throws NoSuchElementException {
+  public static final <T1> T1 head(List<T1> arg) throws NoSuchElementException {
     if (arg instanceof LinkedList) return ((LinkedList<T1>)arg).getFirst();
     if (arg.size() == 0) throw new NoSuchElementException("Empty list");
     return arg.get(0);
@@ -123,7 +124,7 @@
    * @param arg The list.
    * @return The first element in the list, or <code>null</code> if the list is empty.
    */
-  public static <T1> T1 headN(LinkedList<T1> arg) {
+  public static final <T1> T1 headN(LinkedList<T1> arg) {
     return arg.isEmpty() ? null : arg.getFirst();
   }
 
@@ -133,7 +134,7 @@
    * @param arg The list.
    * @return The first element in the list, or <code>null</code> if the list is empty.
    */
-  public static <T1> T1 headN(List<T1> arg) {
+  public static final <T1> T1 headN(List<T1> arg) {
     return arg.isEmpty() ? null : (arg instanceof LinkedList) ? ((LinkedList<T1>)arg).getFirst() : arg.get(0);
   }
 
@@ -144,7 +145,7 @@
    * @return The last element in the list.
    * @throws NoSuchElementException If the list is empty.
    */
-  public static <T1> T1 tail(LinkedList<T1> arg) throws NoSuchElementException {
+  public static final <T1> T1 tail(LinkedList<T1> arg) throws NoSuchElementException {
     return arg.getLast();
   }
 
@@ -155,7 +156,7 @@
    * @return The last element in the list.
    * @throws IndexOutOfBoundsException If the list is empty.
    */
-  public static <T1> T1 tail(List<T1> arg) throws NoSuchElementException {
+  public static final <T1> T1 tail(List<T1> arg) throws NoSuchElementException {
     if (arg instanceof LinkedList) return ((LinkedList<T1>)arg).getLast();
     if (arg.size() == 0) throw new NoSuchElementException("Empty list");
     return arg.get(arg.size() - 1);
@@ -167,7 +168,7 @@
    * @param arg The list.
    * @return The last element in the list, or <code>null</code> if the list is empty.
    */
-  public static <T1> T1 tailN(LinkedList<T1> arg) {
+  public static final <T1> T1 tailN(LinkedList<T1> arg) {
     return arg.isEmpty() ? null : arg.getLast();
   }
 
@@ -177,7 +178,7 @@
    * @param arg The list.
    * @return The last element in the list, or <code>null</code> if the list is empty.
    */
-  public static <T1> T1 tailN(List<T1> arg) {
+  public static final <T1> T1 tailN(List<T1> arg) {
     return arg.isEmpty() ? null : arg.get(arg.size() - 1);
   }
 
@@ -188,7 +189,7 @@
    * @return The first element in the list.
    * @throws NoSuchElementException If the list is empty.
    */
-  public static <T1> T1 first(LinkedList<T1> arg) throws NoSuchElementException {
+  public static final <T1> T1 first(LinkedList<T1> arg) throws NoSuchElementException {
     return arg.getFirst();
   }
 
@@ -199,10 +200,63 @@
    * @return The first element in the collection.
    * @throws NoSuchElementException If the collection is empty.
    */
-  public static <T1> T1 first(Collection<T1> arg) throws NoSuchElementException {
+  public static final <T1> T1 first(Collection<T1> arg) throws NoSuchElementException {
     if (arg instanceof LinkedList) return ((LinkedList<T1>)arg).getFirst();
     if (arg.isEmpty()) throw new NoSuchElementException("Empty Collection");
     return arg.iterator().next();
   }
 
+  /**
+   * Inserts an element into a list in ascending order.
+   * @param <T> The type of the element to be inserted. Must be comparable on itself.
+   * @param list The list to insert into. This must already be ordered.
+   * @param c The element to insert.
+   * @return The newly modified list with all elements in ascending.
+   */
+  public static final <T extends Comparable<T>> List<T> ascendingInsert(List<T> list, T c) {
+    return orderedInsert(list, c, true);
+  }
+
+  /**
+   * Inserts an element into an ordered list in descending order.
+   * @param <T> The type of the element to be inserted. Must be comparable on itself.
+   * @param list The list to insert into. This must already be ordered.
+   * @param c The element to insert.
+   * @return The newly modified list, with all elements in descending order.
+   */
+  public static final <T extends Comparable<T>> List<T> descendingInsert(List<T> list, T c) {
+    return orderedInsert(list, c, false);
+  }
+
+  /**
+   * Inserts an element into an ordered list in a given order.
+   * @param <T> The type of the element to be inserted. Must be comparable on itself.
+   * @param list The list to insert into. This must already be ordered in the same order as this insert.
+   * @param c The element to insert.
+   * @return The newly modified list, with all elements in order.
+   */
+  private static final <T extends Comparable<T>> List<T> orderedInsert(List<T> list, T c, boolean smaller) {
+    ListIterator<T> i = list.listIterator();
+    while (i.hasNext()) {
+      if (orderTest(c.compareTo(i.next()), smaller)) {
+        i.previous();
+        break;
+      }
+    }
+    i.add(c);
+    return list;
+  }
+
+  /**
+   * Tests if a comparison value indicates that smaller or larger values.
+   * @param value The value returned from a comparison.
+   * @param smaller When <code>true</code> this returns true for a comparison indicating a
+   *                smaller value was first, otherwise it tests if the comparison indicates
+   *                a larger value first.
+   * @return <code>true</code> when the value is in the same direction as the smaller flag indicates.
+   */
+  private static final boolean orderTest(int value, boolean smaller) {
+    return smaller ? value < 0 : value > 0;
+  }
+
 }

Modified: trunk/src/jar/util/java/org/mulgara/util/functional/Pair.java
===================================================================
--- trunk/src/jar/util/java/org/mulgara/util/functional/Pair.java	2009-03-24 01:12:37 UTC (rev 1625)
+++ trunk/src/jar/util/java/org/mulgara/util/functional/Pair.java	2009-03-24 18:42:49 UTC (rev 1626)
@@ -46,4 +46,6 @@
   public static <C1,C2> Pair<C1,C2> p(C1 c1, C2 c2) { return new Pair<C1,C2>(c1, c2); }
 
   public Map<T1,T2> addTo(Map<T1,T2> map) { map.put(first, second); return map; }
+
+  public String toString() { return "{" + first + "," + second + "}"; }
 }

Modified: trunk/src/jar/web/java/org/mulgara/webquery/QueryServlet.java
===================================================================
--- trunk/src/jar/web/java/org/mulgara/webquery/QueryServlet.java	2009-03-24 01:12:37 UTC (rev 1625)
+++ trunk/src/jar/web/java/org/mulgara/webquery/QueryServlet.java	2009-03-24 18:42:49 UTC (rev 1626)
@@ -417,7 +417,6 @@
         throw new RequestException("Invalid URI for upload graph. " + e.getInput());
       }
     }
-    System.err.println("No graphs in the parameter values");
     // look in the mime data
     if (mime != null) {
       try {




More information about the Mulgara-svn mailing list