[Mulgara-svn] r1621 - in branches/consistency/src/jar: content-rlog/java/org/mulgara/krule/rlog/ast/output krule/java/org/mulgara/krule

pag at mulgara.org pag at mulgara.org
Wed Mar 18 22:24:11 UTC 2009


Author: pag
Date: 2009-03-18 15:24:10 -0700 (Wed, 18 Mar 2009)
New Revision: 1621

Added:
   branches/consistency/src/jar/krule/java/org/mulgara/krule/ConsistencyCheck.java
Modified:
   branches/consistency/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/output/RuleGenerator.java
   branches/consistency/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/output/RuleWriter.java
   branches/consistency/src/jar/krule/java/org/mulgara/krule/KruleLoader.java
   branches/consistency/src/jar/krule/java/org/mulgara/krule/QueryStruct.java
   branches/consistency/src/jar/krule/java/org/mulgara/krule/Rule.java
Log:
Now writing ConsistencyChecks as a new rule type, and loading them up with the KruleLoader to be run with the rules

Modified: branches/consistency/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/output/RuleGenerator.java
===================================================================
--- branches/consistency/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/output/RuleGenerator.java	2009-03-17 19:54:23 UTC (rev 1620)
+++ branches/consistency/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/output/RuleGenerator.java	2009-03-18 22:24:10 UTC (rev 1621)
@@ -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,9 +35,11 @@
 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.CHECK;
 import static org.mulgara.krule.KruleLoader.CONSTRAINT_CONJUNCTION;
 import static org.mulgara.krule.KruleLoader.DIFFERENCE;
 import static org.mulgara.krule.KruleLoader.HAS_QUERY;
@@ -45,11 +48,13 @@
 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.MINUEND;
 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.SUBTRAHEND;
 import static org.mulgara.krule.KruleLoader.TRIGGERS;
 import static org.mulgara.krule.KruleLoader.VARIABLE;
 
@@ -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: branches/consistency/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/output/RuleWriter.java
===================================================================
--- branches/consistency/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/output/RuleWriter.java	2009-03-17 19:54:23 UTC (rev 1620)
+++ branches/consistency/src/jar/content-rlog/java/org/mulgara/krule/rlog/ast/output/RuleWriter.java	2009-03-18 22:24:10 UTC (rev 1621)
@@ -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>");
   }
 

Added: branches/consistency/src/jar/krule/java/org/mulgara/krule/ConsistencyCheck.java
===================================================================
--- branches/consistency/src/jar/krule/java/org/mulgara/krule/ConsistencyCheck.java	                        (rev 0)
+++ branches/consistency/src/jar/krule/java/org/mulgara/krule/ConsistencyCheck.java	2009-03-18 22:24:10 UTC (rev 1621)
@@ -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: branches/consistency/src/jar/krule/java/org/mulgara/krule/KruleLoader.java
===================================================================
--- branches/consistency/src/jar/krule/java/org/mulgara/krule/KruleLoader.java	2009-03-17 19:54:23 UTC (rev 1620)
+++ branches/consistency/src/jar/krule/java/org/mulgara/krule/KruleLoader.java	2009-03-18 22:24:10 UTC (rev 1621)
@@ -34,6 +34,8 @@
 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;
 
 /**
@@ -124,6 +126,12 @@
   /** 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";
 
@@ -163,6 +171,9 @@
   /** 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";
 
@@ -205,6 +216,12 @@
   /** 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));
 
@@ -244,6 +261,9 @@
   /** 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));
 
@@ -380,7 +400,15 @@
   private RuleStructure findRules() throws QueryException, TuplesException {
     // select $rule from <ruleGraph> where $rule <rdf:type> <krule:Rule>
     Variable ruleV = new Variable("rule");
-    Query query = createQuery(new ConstraintImpl(ruleV, RDF_TYPE, RULE), ruleV);
+    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");
@@ -392,7 +420,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();
@@ -477,34 +509,31 @@
       // 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);
         }
@@ -709,7 +738,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();
@@ -844,30 +873,42 @@
     // build constraints in place, recursively constructing child constraints until all are found
     if (logger.isDebugEnabled()) logger.debug("Querying for Join constraints.");
 
-    // select $constraint $constraint2 $type from <ruleGraph>
-    // where $constraint <krule:argument> $constraint2
+    // 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, ARGUMENT, constraint2V),
+        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, 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, constraint2V, typeV);
+    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>();
@@ -876,13 +917,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();
@@ -890,19 +934,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
@@ -1027,13 +1071,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
@@ -1044,21 +1088,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;
   }
@@ -1080,8 +1130,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);
   }
 
 
@@ -1137,30 +1190,42 @@
 
 
   /**
-   * 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.

Modified: branches/consistency/src/jar/krule/java/org/mulgara/krule/QueryStruct.java
===================================================================
--- branches/consistency/src/jar/krule/java/org/mulgara/krule/QueryStruct.java	2009-03-17 19:54:23 UTC (rev 1620)
+++ branches/consistency/src/jar/krule/java/org/mulgara/krule/QueryStruct.java	2009-03-18 22:24:10 UTC (rev 1621)
@@ -62,7 +62,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 +80,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,19 +89,27 @@
    *         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++) {
@@ -109,6 +117,9 @@
       // check the type
       if (types[i].equals(KruleLoader.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
@@ -122,9 +133,13 @@
 
       } else if (types[i].equals(KruleLoader.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 +158,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 +174,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: branches/consistency/src/jar/krule/java/org/mulgara/krule/Rule.java
===================================================================
--- branches/consistency/src/jar/krule/java/org/mulgara/krule/Rule.java	2009-03-17 19:54:23 UTC (rev 1620)
+++ branches/consistency/src/jar/krule/java/org/mulgara/krule/Rule.java	2009-03-18 22:24:10 UTC (rev 1621)
@@ -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();
   }
 




More information about the Mulgara-svn mailing list