beagle r4585 - in branches/beagle-rdf: . Util Util/SemWeb



Author: dbera
Date: Fri Mar  7 01:12:14 2008
New Revision: 4585
URL: http://svn.gnome.org/viewvc/beagle?rev=4585&view=rev

Log:
Add Euler.cs (distributed under W3C license). This will be useful for inferencing.


Added:
   branches/beagle-rdf/Util/SemWeb/Euler.cs   (contents, props changed)
Modified:
   branches/beagle-rdf/COPYING
   branches/beagle-rdf/Util/Makefile.am

Modified: branches/beagle-rdf/COPYING
==============================================================================
--- branches/beagle-rdf/COPYING	(original)
+++ branches/beagle-rdf/COPYING	Fri Mar  7 01:12:14 2008
@@ -14,6 +14,7 @@
 	* jslib - Mozilla Public License v1.1
 	* xdgmime - Academic Free License v2.0 or GNU LGPL v2
 	* LZMA SDK - GNU LGPL v2
+	* Util/SemWeb/Euler.cs - W3C software license
 
 -----------------------------------------------------------------------------
 

Modified: branches/beagle-rdf/Util/Makefile.am
==============================================================================
--- branches/beagle-rdf/Util/Makefile.am	(original)
+++ branches/beagle-rdf/Util/Makefile.am	Fri Mar  7 01:12:14 2008
@@ -149,6 +149,7 @@
 	$(srcdir)/SemWeb/Algos.cs               \
 	$(srcdir)/SemWeb/SparqlClient.cs	\
 	$(srcdir)/SemWeb/XPathSemWebNavigator.cs\
+	$(srcdir)/SemWeb/Euler.cs		\
 	$(srcdir)/QueryKeywordMapping.cs
 
 # Semweb compile flag

Added: branches/beagle-rdf/Util/SemWeb/Euler.cs
==============================================================================
--- (empty file)
+++ branches/beagle-rdf/Util/SemWeb/Euler.cs	Fri Mar  7 01:12:14 2008
@@ -0,0 +1,663 @@
+// Adapted from:
+// ---------------------------------------------------------------
+// Euler proof mechanism -- Jos De Roo
+// version = '$Id: euler.js,v 1.35 2006/12/17 01:25:01 josd Exp $'
+// http://eulersharp.cvs.sourceforge.net/eulersharp/2006/02swap/euler.js?view=log
+// ---------------------------------------------------------------
+// The original Euler code is licensed under the W3C Software License.
+// This is a very liberal translation of the original code into C#.
+
+using System;
+using System.Collections;
+
+using SemWeb;
+using SemWeb.Util;
+
+namespace SemWeb.Inference {
+	
+	public class Euler : Reasoner {
+	
+		static bool Debug = System.Environment.GetEnvironmentVariable("SEMWEB_DEBUG_EULER") != null;
+
+		Hashtable rules;
+		
+		static Hashtable builtInRelations;
+		
+		static Euler() {
+			RdfRelation[] rs = new RdfRelation[] {
+				new SemWeb.Inference.Relations.MathAbsoluteValueRelation(), new SemWeb.Inference.Relations.MathCosRelation(), new SemWeb.Inference.Relations.MathDegreesRelation(), new SemWeb.Inference.Relations.MathEqualToRelation(),
+				new SemWeb.Inference.Relations.MathNegationRelation(), new SemWeb.Inference.Relations.MathRoundedRelation(), new SemWeb.Inference.Relations.MathSinRelation(), new SemWeb.Inference.Relations.MathSinhRelation(), new SemWeb.Inference.Relations.MathTanRelation(), new SemWeb.Inference.Relations.MathTanhRelation(),
+				new SemWeb.Inference.Relations.MathAtan2Relation(), new SemWeb.Inference.Relations.MathDifferenceRelation(), new SemWeb.Inference.Relations.MathExponentiationRelation(), new SemWeb.Inference.Relations.MathIntegerQuotientRelation(),
+				new SemWeb.Inference.Relations.MathQuotientRelation(), new SemWeb.Inference.Relations.MathRemainderRelation(),
+				new SemWeb.Inference.Relations.MathSumRelation(), new SemWeb.Inference.Relations.MathProductRelation(),
+				new SemWeb.Inference.Relations.MathGreaterThanRelation(), new SemWeb.Inference.Relations.MathLessThanRelation(), new SemWeb.Inference.Relations.MathNotGreaterThanRelation(), new SemWeb.Inference.Relations.MathNotLessThanRelation(), new SemWeb.Inference.Relations.MathNotEqualToRelation()
+			};
+		
+			builtInRelations = new Hashtable();
+			foreach (RdfRelation r in rs)
+				builtInRelations[r.Uri] = r;
+		}
+		
+		public Euler(StatementSource rules) {
+			this.rules = RulesToCases(rules);
+		}
+		
+		public override bool Distinct { get { return false; } } // not sure...
+		
+		public override void Select(SelectFilter filter, SelectableSource targetModel, StatementSink sink) {
+			if (filter.Subjects == null) filter.Subjects = new Entity[] { new Variable("subject") };
+			if (filter.Predicates == null) filter.Predicates = new Entity[] { new Variable("predicate") };
+			if (filter.Objects == null) filter.Objects = new Entity[] { new Variable("object") };
+
+			if (filter.Metas == null) filter.Metas = new Entity[] { Statement.DefaultMeta };
+			
+			foreach (Statement s in filter) { // until we can operate on filter directly
+				ArrayList evidence = prove(rules, targetModel, new Statement[] { s }, -1);
+				if (evidence == null)
+					continue; // not provable (in max number of steps, if that were given)
+				
+				foreach (EvidenceItem ei in evidence) {
+					foreach (Statement h in ei.head) { // better be just one statement
+						if (filter.LiteralFilters != null
+							&& !LiteralFilter.MatchesFilters(h.Object, filter.LiteralFilters, targetModel))
+							continue;
+						
+						sink.Add(h);
+					}
+				}
+			}
+		}
+		
+		private void QueryCheckArg(Statement[] graph) {
+			if (graph == null) throw new ArgumentNullException("graph");
+			foreach (Statement s in graph) {
+				if (s.Subject == null || s.Predicate == null || s.Object == null || s.Meta == null)
+					throw new ArgumentNullException("Graph statements cannot contain a null subject, predicate, or object. Use a Variable instance instead.");
+				if (s.Meta != Statement.DefaultMeta && !(s.Meta is Variable))
+					throw new NotSupportedException("Graph statements' meta fields must be Statement.DefaultMeta. Other values of meta are not currently supported.");
+			}
+		}
+		
+		public override SemWeb.Query.MetaQueryResult MetaQuery(Statement[] graph, SemWeb.Query.QueryOptions options, SelectableSource targetModel) {
+			QueryCheckArg(graph);
+			SemWeb.Query.MetaQueryResult ret = new SemWeb.Query.MetaQueryResult();
+			ret.QuerySupported = true;
+			// TODO: Best to check also whether variables in the query are even known to us.
+			return ret;
+		}
+		
+		public override void Query(Statement[] graph, SemWeb.Query.QueryOptions options, SelectableSource targetModel, SemWeb.Query.QueryResultSink sink) {
+			QueryCheckArg(graph);
+
+			// Try to do the inferencing.
+			ArrayList evidence = prove(rules, targetModel, graph, -1);
+			if (evidence == null)
+				return; // not provable (in max number of steps, if that were given)
+			
+			// Then send the possible bindings to the QueryResultSink.
+			
+			// Map variables to indexes.
+			Hashtable vars = new Hashtable();
+			foreach (Statement s in graph) {
+				if (s.Subject is Variable && !vars.ContainsKey(s.Subject)) vars[s.Subject] = vars.Count;
+				if (s.Predicate is Variable && !vars.ContainsKey(s.Predicate)) vars[s.Predicate] = vars.Count;
+				if (s.Object is Variable && !vars.ContainsKey(s.Object)) vars[s.Object] = vars.Count;
+			}
+			
+			// Prepare the bindings array.
+			Variable[] varOrder = new Variable[vars.Count];
+			foreach (Variable v in vars.Keys)
+				varOrder[(int)vars[v]] = v;
+			
+			// Initialize the sink.
+			sink.Init(varOrder);
+			
+			// Send a binding set for each piece of evidence.
+			foreach (EvidenceItem ei in evidence) {
+				// Write a comment to the results with the actual proof. (nifty actually)
+				sink.AddComments(ei.ToProof().ToString());
+			
+				// Create the binding array and send it on
+				Resource[] variableBindings = new Resource[varOrder.Length];
+				foreach (Variable v in vars.Keys)
+					if (ei.env.ContainsKey(v))
+						variableBindings[(int)vars[v]] = (Resource)ei.env[v];
+				sink.Add(new SemWeb.Query.VariableBindings(varOrder, variableBindings));
+			}
+			
+			// Close the sink.
+			sink.Finished();
+		}
+		
+		// Static methods that wrap the Euler engine
+		
+		private static readonly Entity entLOGIMPLIES = "http://www.w3.org/2000/10/swap/log#implies";;
+		private static readonly Entity entRDFFIRST = "http://www.w3.org/1999/02/22-rdf-syntax-ns#first";;
+		private static readonly Entity entRDFREST = "http://www.w3.org/1999/02/22-rdf-syntax-ns#rest";;
+		private static readonly Entity entRDFNIL = "http://www.w3.org/1999/02/22-rdf-syntax-ns#nil";;
+
+		private class Sequent {
+			public Statement head; // consequent
+			public Statement[] body; // antecedent
+			public Hashtable callArgs; // encapsulates (...) arguments to user predicates
+			
+			public Sequent(Statement head, Statement[] body, Hashtable callArgs) {
+				this.head = head;
+				this.body = body;
+				this.callArgs = callArgs;
+			}
+			public Sequent(Statement head) {
+				this.head = head;
+				this.body = new Statement[0];
+			}
+			
+			public Rule ToRule() {
+				return new Rule(body, new Statement[] { head });
+			}
+			
+			// override to satisfy a warning about overloading ==
+			public override bool Equals(object o) {
+				return this == (Sequent)o;
+			}
+			
+			// override to satisfy a warning about overloading ==
+			public override int GetHashCode() {
+				return base.GetHashCode();
+			}
+			
+			public override string ToString() {
+				string ret = "{ ";
+				foreach (Statement b in body) {
+					if (ret != "{ ") ret += ", ";
+					ret += b.ToString();
+				}
+				ret += " } => " + head;
+				return ret;
+			}
+			
+			public static bool operator ==(Sequent a, Sequent b) {
+				if (object.ReferenceEquals(a, b)) return true;
+				if (object.ReferenceEquals(a, null) && object.ReferenceEquals(b, null)) return true;
+				if (object.ReferenceEquals(a, null) || object.ReferenceEquals(b, null)) return false;
+				if (a.head != b.head) return false;
+				if (a.body.Length != b.body.Length) return false;
+				for (int i = 0; i < a.body.Length; i++)
+					if (a.body[i] != b.body[i]) return false;
+				return true;
+			}
+			public static bool operator !=(Sequent a, Sequent b) {
+				return !(a == b);
+			}
+		}
+		
+		private class Ground {
+			public Sequent src;  // evidence
+			public Hashtable env;  // substitution environment: Resource => Resource
+			
+			public Ground(Sequent src, Hashtable env) {
+				this.src = src;
+				this.env = env;
+			}
+			
+			public ProofStep ToProofStep() {
+				return new ProofStep(src.ToRule(), env);
+			}
+		}
+		
+		private class EvidenceItem {
+			public Statement[] head;
+			public ArrayList body; // of Ground
+			public Hashtable env; // substitutions of goal-level variables
+			
+			public Proof ToProof() {
+				ProofStep[] steps = new ProofStep[body.Count];
+				for (int i = 0; i < body.Count; i++)
+					steps[i] = ((Ground)body[i]).ToProofStep();
+				return new Proof(head, steps);
+			}
+			
+			public override string ToString() {
+				string ret = "";
+				foreach (DictionaryEntry kv in env)
+					ret += "\t" + kv.Key + "=>" + kv.Value + "\n";
+				return ret;
+			}
+		}
+		
+		private class QueueItem {
+			public Sequent rule;
+			public Sequent src;
+			public int ind;
+			public QueueItem parent;
+			public Hashtable env; // substitution environment: Resource => Resource
+			public ArrayList ground;
+			
+			public QueueItem Clone() {
+				return (QueueItem)MemberwiseClone();
+			}
+			
+			public override string ToString() {
+				string ret = "";
+				foreach (DictionaryEntry kv in env)
+					ret += kv.Key + "=>" + kv.Value + "; ";
+				ret += rule.ToString() + " @ " + ind + "/" + rule.body.Length;
+				return ret;
+			}
+		}
+		
+		public Proof[] Prove(SelectableSource world, Statement[] goal) {
+			ArrayList evidence = prove(rules, world, goal, -1);
+			if (evidence == null)
+				throw new Exception("Could not complete proof within the maximum number of steps allowed.");
+			
+			ArrayList ret = new ArrayList();
+			foreach (EvidenceItem ei in evidence)
+				ret.Add(ei.ToProof());
+
+			return (Proof[])ret.ToArray(typeof(Proof));
+		}
+		
+		private static ArrayList prove(Hashtable cases, SelectableSource world, Statement[] goal, int maxNumberOfSteps) {
+			// This is the main routine from euler.js.
+		
+			// cases: Resource (predicate) => IList of Sequent
+			
+			if (world != null) // cache our queries to the world, if a world is provided
+				world = new SemWeb.Stores.CachedSource(world);
+		
+			// Create the queue and add the first item.
+			ArrayList queue = new ArrayList();
+			{
+				QueueItem start = new QueueItem();
+				start.env = new Hashtable();
+				start.rule = new Sequent(Statement.All, goal, null);
+				start.src = null;
+				start.ind = 0;
+				start.parent = null;
+				start.ground = new ArrayList();
+				queue.Add(start);
+				if (Debug) Console.Error.WriteLine("Euler: Queue: " + start);
+			}
+			
+			// The evidence array holds the results of this proof.
+			ArrayList evidence = new ArrayList();
+			
+			// Track how many steps we take to not go over the limit.
+			int step = 0;
+			
+			// Process the queue until it's empty or we reach our step limit.
+			while (queue.Count > 0) {
+				// deal with the QueueItem at the top of the queue
+				QueueItem c = (QueueItem)queue[queue.Count-1];
+				queue.RemoveAt(queue.Count-1);
+				ArrayList g = new ArrayList(c.ground);
+				
+				// have we done too much?
+				step++;
+				if (maxNumberOfSteps != -1 && step >= maxNumberOfSteps) {
+					if (Debug) Console.Error.WriteLine("Euler: Maximum number of steps exceeded.  Giving up.");
+					return null;
+				}
+				
+				// if each statement in the body of the sequent has been proved
+				if (c.ind == c.rule.body.Length) {
+					// if this is the top-level sequent being proved; we've found a complete evidence for the goal
+					if (c.parent == null) {
+						EvidenceItem ev = new EvidenceItem();
+						ev.head = new Statement[c.rule.body.Length];
+						bool canRepresentHead = true;
+						for (int i = 0; i < c.rule.body.Length; i++) {
+							ev.head[i] = evaluate(c.rule.body[i], c.env);
+							if (ev.head[i].AnyNull) // can't represent it: literal in subject position, for instance
+								canRepresentHead = false;
+						}
+						
+						ev.body = c.ground;
+						ev.env = c.env;
+
+						if (Debug) Console.Error.WriteLine("Euler: Found Evidence: " + ev);
+						
+						if (!canRepresentHead)
+							continue;
+
+						evidence.Add(ev);
+					
+					// this is a subproof of something; whatever it is a subgroup for can
+					// be incremented
+					} else {
+						// if the rule had no body, it was just an axiom and we represent that with Ground
+						if (c.rule.body.Length != 0) g.Add(new Ground(c.rule, c.env));
+
+						// advance the parent being proved and put the advanced
+						// parent into the queue; unify the parent variable assignments
+						// with this one's
+						QueueItem r = new QueueItem();
+						r.rule = c.parent.rule;
+						r.src = c.parent.src;
+						r.ind = c.parent.ind;
+						r.parent = c.parent.parent != null
+							? c.parent.parent.Clone()
+							: null;
+						r.env = (Hashtable)c.parent.env.Clone();
+						r.ground = g;
+						unify(c.rule.head, c.env, r.rule.body[r.ind], r.env, true);
+						r.ind++;
+						queue.Add(r);
+						if (Debug) Console.Error.WriteLine("Euler: Queue Advancement: " + r);
+					}
+				
+				// this sequent still has parts of the body left to be proved; try to
+				// find evidence for the next statement in the body, and if we find
+				// evidence, queue up that evidence
+				} else {
+					// this is the next part of the body that we need to try to prove
+					Statement t = c.rule.body[c.ind];
+					
+					// Try to process this predicate with a user-provided custom
+					// function that resolves things like mathematical relations.
+					// euler.js provides similar functionality, but the system
+					// of user predicates is completely different here.
+					RdfRelation b = FindUserPredicate(t.Predicate);
+					if (b != null) {
+						Resource[] args;
+						Variable[] unifyResult;
+						Resource value = evaluate(t.Object, c.env);
+						
+						if (!c.rule.callArgs.ContainsKey(t.Subject)) {
+							// The array of arguments to this relation is just the subject of the triple itself
+							args = new Resource[] { evaluate(t.Subject, c.env) };
+							unifyResult = new Variable[1];
+							if (t.Subject is Variable) unifyResult[0] = (Variable)t.Subject;
+						
+						} else {
+							// The array of arguments to this relation comes from a pre-grouped arg list.
+							args = (Resource[])c.rule.callArgs[t.Subject];
+							unifyResult = new Variable[args.Length];
+							
+							for (int i = 0; i < args.Length; i++) {
+								if (args[i] is Variable) {
+									unifyResult[i] = (Variable)args[i];
+									args[i] = evaluate(args[i], c.env);
+								}
+							}
+						}
+						
+						// Run the user relation on the array of arguments (subject) and on the object.
+						if (b.Evaluate(args, ref value)) {
+							// If it succeeds, we press on.
+						
+							// The user predicate may update the 'value' variable and the argument
+							// list array, and so we want to unify any variables in the subject
+							// or object of this user predicate with the values given to us
+							// by the user predicate.
+							Hashtable newenv = (Hashtable)c.env.Clone();
+							if (t.Object is Variable) unify(value, null, t.Object, newenv, true);
+							for (int i = 0; i < args.Length; i++)
+								if (unifyResult[i] != null)
+									unify(args[i], null, unifyResult[i], newenv, true);
+						
+							Statement grnd = evaluate(t, newenv);
+							if (grnd != Statement.All) // sometimes not representable, like if literal as subject
+								g.Add(new Ground(new Sequent(grnd, new Statement[0], null), new Hashtable()));
+
+							QueueItem r = new QueueItem();
+							r.rule = c.rule;
+							r.src = c.src;
+							r.ind = c.ind;
+							r.parent = c.parent;
+							r.env = newenv;
+							r.ground = g;
+							r.ind++;
+							queue.Add(r);
+						}
+						continue;
+					}
+					
+					// t can be proved either by the use of a rule
+					// or if t literally exists in the world
+
+					Statement t_resolved = evaluate(t, c.env);
+					
+					// If resolving this statement requires putting a
+					// literal in subject or predicate position, we
+					// can't prove it.
+					if (t_resolved == Statement.All)
+						continue;
+						
+					ArrayList tcases = new ArrayList();
+					
+					// get all of the rules that apply to the predicate in question
+					if (t_resolved.Predicate != null && cases.ContainsKey(t_resolved.Predicate))
+						tcases.AddRange((IList)cases[t_resolved.Predicate]);
+
+					/*if (cases.ContainsKey("WILDCARD")) // not supported yet -- infinite regress not handled
+						tcases.AddRange((IList)cases["WILDCARD"]);*/
+					
+					// if t has no unbound variables and we've matched something from
+					// the axioms, don't bother looking at the world, and don't bother
+					// proving it any other way than by the axiom.
+					bool lookAtWorld = true;
+					foreach (Sequent seq in tcases) {
+						if (seq.body.Length == 0 && seq.head == t_resolved) {
+							lookAtWorld = false;
+							tcases.Clear();
+							tcases.Add(seq);
+							break;
+						}
+					}
+
+					// if there is a seprate world, get all of the world
+					// statements that witness t
+					if (world != null && lookAtWorld) {
+						MemoryStore w = new MemoryStore();
+					
+						if (Debug) Console.WriteLine("Euler: Ask World: " + t_resolved);
+						world.Select(t_resolved, w);
+						foreach (Statement s in w) {
+							if (Debug) Console.WriteLine("Euler: World Select Response:  " + s);
+							Sequent seq = new Sequent(s);
+							tcases.Add(seq);
+						}
+					}
+
+					// If there is no evidence or potential evidence (i.e. rules)
+					// for t, then we will dump this QueueItem by not queuing any
+					// subproofs.
+					
+					// Otherwise we try each piece of evidence in turn.
+					foreach (Sequent rl in tcases) {
+						ArrayList g2 = (ArrayList)c.ground.Clone();
+						if (rl.body.Length == 0) g2.Add(new Ground(rl, new Hashtable()));
+						
+						QueueItem r = new QueueItem();
+						r.rule = rl;
+						r.src = rl;
+						r.ind = 0;
+						r.parent = c;
+						r.env = new Hashtable();
+						r.ground = g2;
+						
+						if (unify(t, c.env, rl.head, r.env, true)) {
+							QueueItem ep = c;  // euler path
+						 	while ((ep = ep.parent) != null)
+						  		if (ep.src == c.src && unify(ep.rule.head, ep.env, c.rule.head, c.env, false))
+						  			break;
+						 	if (ep == null) {
+						 		queue.Insert(0, r);
+						 		if (Debug) Console.Error.WriteLine("Euler: Queue from Axiom: " + r);
+						 	}
+						}
+					}
+				}
+			}
+			
+			return evidence;
+		}
+		
+		private static bool unify(Resource s, Hashtable senv, Resource d, Hashtable denv, bool f) {
+			if (s is Variable) {
+				Resource sval = evaluate(s, senv);
+				if (sval != null) return unify(sval, senv, d, denv, f);
+				else return true;
+			} else if (d is Variable) {
+				Resource dval = evaluate(d, denv);
+				if (dval != null) {
+					return unify(s, senv, dval, denv, f);
+				} else {
+					if (f) denv[d] = evaluate(s, senv);
+					return true;
+				}
+			} else if (s.Equals(d)) {
+				return true;
+			} else {
+				return false;
+			}
+		}
+
+		private static bool unify(Statement s, Hashtable senv, Statement d, Hashtable denv, bool f) {
+			if (s == Statement.All) return false;
+			if (!unify(s.Subject, senv, d.Subject, denv, f)) return false;
+			if (!unify(s.Predicate, senv, d.Predicate, denv, f)) return false;
+			if (!unify(s.Object, senv, d.Object, denv, f)) return false;
+			return true;
+		}
+		
+		private static Resource evaluate(Resource t, Hashtable env) {
+			if (t is Variable) {
+				Resource val = (Resource)env[t];
+				if (val != null)
+					return evaluate(val, env);
+				else
+					return null;
+			}
+			return t;
+		}
+		
+		private static Statement evaluate(Statement t, Hashtable env) {
+			Resource s = evaluate(t.Subject, env);
+			Resource p = evaluate(t.Predicate, env);
+			Resource o = evaluate(t.Object, env);
+		
+			// If we can't represent this statement because it requires
+			// putting a literal in subject or predicate position,
+			// return Statement.All (i.e. null S/P/O).
+			if (s is Literal || p is Literal)
+				return Statement.All;
+				
+			return new Statement((Entity)s, (Entity)p, o, Statement.DefaultMeta);
+		}
+		
+		// The next few routines convert a set of axioms from a StatementSource
+		// into a data structure of use for the algorithm, with Sequents and things.
+		
+		
+
+		private static Hashtable RulesToCases(StatementSource rules) {
+			Hashtable cases = new Hashtable();
+			MemoryStore rules_store = new MemoryStore(rules);
+			foreach (Statement p in rules_store) {
+				if (p.Meta == Statement.DefaultMeta) {
+					if (p.Predicate == entLOGIMPLIES && p.Object is Entity) {
+						MemoryStore body = new MemoryStore();
+						MemoryStore head = new MemoryStore();
+					
+						rules_store.Select(new Statement(null, null, null,  (Entity)p.Subject), new RemoveMeta(body));
+						rules_store.Select(new Statement(null, null, null,  (Entity)p.Object), new RemoveMeta(head));
+						
+						// Any variables in the head not bound in the body represent existentially closed bnodes.
+						// (Euler's OWL test case does this. Wish they had used bnodes instead of vars...)
+						ResSet bodyvars = new ResSet();
+						foreach (Statement b in body) {
+							if (b.Subject is Variable) bodyvars.Add(b.Subject);
+							if (b.Predicate is Variable) bodyvars.Add(b.Predicate);
+							if (b.Object is Variable) bodyvars.Add(b.Object);
+						}
+						foreach (Entity v in head.GetEntities()) {
+							if (v is Variable && !bodyvars.Contains(v))
+								head.Replace(v, new BNode(((Variable)v).LocalName));
+						}
+						
+						// Replace (...) lists in the body that are tied to the subjects
+						// of user predicates with callArgs objects.
+						Hashtable callArgs = new Hashtable();
+						CollectCallArgs(body, callArgs);
+						
+						// Rules can't have more than one statement in their
+						// consequent.  The best we can do is break up
+						// the consequent into multiple rules.  (Since all head
+						// variables are bound in body, it's equivalent...?)
+						foreach (Statement h in head)
+							AddSequent(cases, new Sequent(h, body.ToArray(), callArgs));
+					} else {
+						AddSequent(cases, new Sequent(p, new Statement[0], null));
+					}
+				}
+			}
+			
+			return cases;
+		}
+
+		private class RemoveMeta : StatementSink {
+			StatementSink sink;
+			public RemoveMeta(StatementSink s) { sink = s; }
+			public bool Add(Statement s) {
+				s.Meta = Statement.DefaultMeta;
+				return sink.Add(s);
+			}
+		}
+		
+		private static void CollectCallArgs(MemoryStore body, Hashtable callArgs) {
+			foreach (Statement s in new MemoryStore(body)) { // make a copy since we remove statements from b within
+				if (FindUserPredicate(s.Predicate) == null) continue;
+				
+				Entity subj = s.Subject;
+				if (!(subj is BNode)) continue; // it would be nice to exclude variables, but we've already converted bnodes to variables
+				
+				BNode head = (BNode)subj;
+				
+				ArrayList argList = new ArrayList();
+				
+				while (true) {
+					Resource[] objs = body.SelectObjects(subj, entRDFFIRST);
+					if (objs.Length == 0) break; // if this is the first iteration, then we're just not looking at a list
+												 // on later iterations, the list must not be well-formed, so we'll just
+												 // stop reading it here
+					
+					argList.Add(objs[0]); // assume a properly formed list
+					body.Remove(new Statement(subj, entRDFFIRST, null));
+					
+					Resource[] rests = body.SelectObjects(subj, entRDFREST);
+					if (rests.Length == 0) break; // not well formed
+					body.Remove(new Statement(subj, entRDFREST, null));
+					if (rests[0] == entRDFNIL) break; // that's the end of the list
+					if (!(rests[0] is Entity)) break; // also not well formed
+					
+					subj = (Entity)rests[0];
+				}
+				
+				if (argList.Count > 0)
+					callArgs[head] = argList.ToArray(typeof(Resource));
+			}
+		}
+		
+		private static void AddSequent(Hashtable cases, Sequent s) {
+			object key = s.head.Predicate;
+			if (key is Variable) key = "WILDCARD";
+			ArrayList list = (ArrayList)cases[key];
+			if (list == null) {
+				list = new ArrayList();
+				cases[key] = list;
+			}
+			list.Add(s);
+		}
+
+		// Lastly, we have special and built-in predicates.
+
+		private static RdfRelation FindUserPredicate(Entity predicate) {
+			if (predicate.Uri == null) return null;
+			if (builtInRelations.ContainsKey(predicate.Uri))
+				return (RdfRelation)builtInRelations[predicate.Uri];
+			return null;
+		}
+	}
+}



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]