beagle r4770 - in trunk/beagle: . Util/SemWeb



Author: dbera
Date: Mon Jun  2 11:28:03 2008
New Revision: 4770
URL: http://svn.gnome.org/viewvc/beagle?rev=4770&view=rev

Log:
Update SemWeb. Make a note that we are incorporating SemWeb under the GPLv3 license. Merge the upstream changes into a single file.


Added:
   trunk/beagle/Util/SemWeb/README.txt
      - copied, changed from r4763, /trunk/beagle/Util/SemWeb/README
   trunk/beagle/Util/SemWeb/upstream-change.diff
Removed:
   trunk/beagle/Util/SemWeb/README
   trunk/beagle/Util/SemWeb/upstream-change-01.diff
   trunk/beagle/Util/SemWeb/upstream-change-02.diff
Modified:
   trunk/beagle/COPYING
   trunk/beagle/Util/SemWeb/AssemblyInfo.cs
   trunk/beagle/Util/SemWeb/Euler.cs
   trunk/beagle/Util/SemWeb/GraphMatch.cs
   trunk/beagle/Util/SemWeb/Inference.cs
   trunk/beagle/Util/SemWeb/Interfaces.cs
   trunk/beagle/Util/SemWeb/N3Reader.cs
   trunk/beagle/Util/SemWeb/N3Writer.cs
   trunk/beagle/Util/SemWeb/Query.cs
   trunk/beagle/Util/SemWeb/RdfReader.cs
   trunk/beagle/Util/SemWeb/RdfXmlReader.cs
   trunk/beagle/Util/SemWeb/Resource.cs
   trunk/beagle/Util/SemWeb/SQLStore.cs
   trunk/beagle/Util/SemWeb/SparqlClient.cs
   trunk/beagle/Util/SemWeb/SpecialRelations.cs
   trunk/beagle/Util/SemWeb/Store.cs
   trunk/beagle/Util/SemWeb/UriMap.cs
   trunk/beagle/Util/SemWeb/Util.cs

Modified: trunk/beagle/COPYING
==============================================================================
--- trunk/beagle/COPYING	(original)
+++ trunk/beagle/COPYING	Mon Jun  2 11:28:03 2008
@@ -9,7 +9,7 @@
 	* Hal# - MIT License
 	* HtmlAgilityPack - BSD License
 	* Parts of libegg (eggaccelerators.c and eggtrayicon.c) - GNU LGPL v2
-	* SemWeb - Creative Commons Attribution License
+	* SemWeb - GPLv3
 	* System.Windows.Forms.RTF - MIT License
 	* jslib - Mozilla Public License v1.1
 	* xdgmime - Academic Free License v2.0 or GNU LGPL v2

Modified: trunk/beagle/Util/SemWeb/AssemblyInfo.cs
==============================================================================
--- trunk/beagle/Util/SemWeb/AssemblyInfo.cs	(original)
+++ trunk/beagle/Util/SemWeb/AssemblyInfo.cs	Mon Jun  2 11:28:03 2008
@@ -10,7 +10,7 @@
 [assembly: AssemblyTrademark("")]
 [assembly: AssemblyCulture("")]		
 
-[assembly: AssemblyVersion("1.0.3.0")]
+[assembly: AssemblyVersion("1.0.6.0")]
 [assembly: AssemblyDelaySign(false)]
 [assembly: AssemblyKeyFile("")]
 [assembly: AssemblyKeyName("")]

Modified: trunk/beagle/Util/SemWeb/Euler.cs
==============================================================================
--- trunk/beagle/Util/SemWeb/Euler.cs	(original)
+++ trunk/beagle/Util/SemWeb/Euler.cs	Mon Jun  2 11:28:03 2008
@@ -17,7 +17,11 @@
 	
 	public class Euler : Reasoner {
 	
+		#if !SILVERLIGHT
 		static bool Debug = System.Environment.GetEnvironmentVariable("SEMWEB_DEBUG_EULER") != null;
+		#else
+		static bool Debug = false;
+		#endif
 
 		Hashtable rules;
 		
@@ -25,12 +29,14 @@
 		
 		static Euler() {
 			RdfRelation[] rs = new RdfRelation[] {
+				#if !SILVERLIGHT
 				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()
+				#endif
 			};
 		
 			builtInRelations = new Hashtable();
@@ -232,9 +238,8 @@
 			public Hashtable env; // substitution environment: Resource => Resource
 			public ArrayList ground;
 			
-			public QueueItem Clone() {
-				return (QueueItem)MemberwiseClone();
-			}
+			public int liveChildren = 0;
+			public StatementList solutions;
 			
 			public override string ToString() {
 				string ret = "";
@@ -265,6 +270,8 @@
 			if (world != null) // cache our queries to the world, if a world is provided
 				world = new SemWeb.Stores.CachedSource(world);
 		
+			StatementMap cached_subproofs = new StatementMap();
+			
 			// Create the queue and add the first item.
 			ArrayList queue = new ArrayList();
 			{
@@ -335,15 +342,20 @@
 						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.parent = c.parent.parent;
 						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);
+						
+						// The number of live children for this parent is decremented since we are
+						// done with this subproof, but we store the result for later.
+						if (c.parent.solutions == null)
+							c.parent.solutions = new StatementList();
+						c.parent.solutions.Add(evaluate(r.rule.body[r.ind-1], r.env));
+						decrementLife(c.parent, cached_subproofs);
 					}
 				
 				// this sequent still has parts of the body left to be proved; try to
@@ -409,6 +421,12 @@
 							r.ground = g;
 							r.ind++;
 							queue.Add(r);
+							
+							// Note: Since we are putting something back in for c, we don't touch the life counter on the parent.
+							
+						} else {
+							// If the predicate fails, decrement the life of the parent.
+							decrementLife(c.parent, cached_subproofs);
 						}
 						continue;
 					}
@@ -418,48 +436,64 @@
 
 					Statement t_resolved = evaluate(t, c.env);
 					
-					// If resolving this statement requires putting a
-					// literal in subject or predicate position, we
+					// If resolving this statement requires putting a literal in subject or predicate position, we
 					// can't prove it.
-					if (t_resolved == Statement.All)
+					if (t_resolved == Statement.All) {
+						decrementLife(c.parent, cached_subproofs);
 						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;
+					// See if we have already tried to prove this.
+					if (cached_subproofs.ContainsKey(t_resolved)) {
+						StatementList cached_solutions = (StatementList)cached_subproofs[t_resolved];
+						if (cached_solutions == null) {
+							if (Debug) Console.Error.WriteLine("Euler: Dropping queue item because we have already failed to prove it: " + t_resolved);
+						} else {
+							foreach (Statement s in cached_solutions) {
+								if (Debug) Console.Error.WriteLine("Euler: Using Cached Axiom:  " + s);
+								Sequent seq = new Sequent(s);
+								tcases.Add(seq);
+							}
 						}
-					}
-
-					// 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);
+					} else {
+						// 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"))
+							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.Error.WriteLine("Running " + c);
+							if (Debug) Console.Error.WriteLine("Euler: Ask World: " + t_resolved);
+							world.Select(t_resolved, w);
+							foreach (Statement s in w) {
+								if (Debug) Console.Error.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.
@@ -483,17 +517,35 @@
 						  		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);
+								// It is better for caching subproofs to work an entire proof out before
+								// going on, which means we want to put the new queue item at the
+								// top of the stack.
+						 		queue.Add(r);
+								c.liveChildren++;
 						 		if (Debug) Console.Error.WriteLine("Euler: Queue from Axiom: " + r);
 						 	}
 						}
 					}
+					
+					// If we did not add anything back into the queue for this item, then
+					// we decrement the life of the parent.
+					if (c.liveChildren == 0)
+						decrementLife(c.parent, cached_subproofs);
 				}
 			}
 			
 			return evidence;
 		}
 		
+		private static void decrementLife(QueueItem q, StatementMap cached_subproofs) {
+			q.liveChildren--;
+			if (q.liveChildren == 0) {
+				Statement t = evaluate(q.rule.body[q.ind], q.env);
+				cached_subproofs[t] = q.solutions;
+				if (Debug && q.solutions == null) Console.Error.WriteLine("Euler: Died: " + q);
+			}
+		}
+		
 		private static bool unify(Resource s, Hashtable senv, Resource d, Hashtable denv, bool f) {
 			if (s is Variable) {
 				Resource sval = evaluate(s, senv);

Modified: trunk/beagle/Util/SemWeb/GraphMatch.cs
==============================================================================
--- trunk/beagle/Util/SemWeb/GraphMatch.cs	(original)
+++ trunk/beagle/Util/SemWeb/GraphMatch.cs	Mon Jun  2 11:28:03 2008
@@ -121,13 +121,23 @@
 		}
 		
 		public override void Run(SelectableSource targetModel, QueryResultSink result) {
-			QueryPart[] parts = new QueryPart[graph.Count];
-			for (int i = 0; i < graph.Count; i++)
-				parts[i] = new QueryPart(graph[i], targetModel);
+			Statement[] newgraph = ReorderQuery(graph.ToArray(), toArray(knownValues), targetModel, null);
+		
+			QueryPart[] parts = new QueryPart[newgraph.Length];
+			for (int i = 0; i < newgraph.Length; i++)
+				parts[i] = new QueryPart(newgraph[i], targetModel);
 			
 			RunGeneralQuery(parts, knownValues, litFilters, distinguishedVars.Count == 0 ? null : distinguishedVars, ReturnStart, ReturnLimit, false, result);
 		}
 		
+		internal static Variable[] toArray(VarKnownValuesType2 kv) {
+			Variable[] ret = new Variable[kv.Count];
+			int ctr = 0;
+			foreach (Variable v in kv.Keys)
+				ret[ctr++] = v;
+			return ret;
+		}
+
 		internal struct QueryPart {
 			public readonly Statement[] Graph;
 			public readonly SelectableSource[] Sources;
@@ -369,7 +379,11 @@
 					
 					vars = null;
 					matches = null;
+					string src = "";
 					foreach (QueryableSource source in part.Sources) {
+						if (src != "") src += ", ";
+						src += source;
+					
 						if (localLimit > 0) {
 							opts.Limit = localLimit - (matches == null ? 0 : matches.Count);
 							if (opts.Limit <= 0)
@@ -407,7 +421,7 @@
 					foreach (Statement s in part.Graph)
 						qs += "\n\t" + s;
 
-					result.AddComments("QUERY: " + qs + (returnLimit > 0 ? " [limit " + returnLimit + "/" + localLimit + "]" : "") + "  => " + matches.Count);
+					result.AddComments("QUERY: " + src + qs + (returnLimit > 0 ? " [limit " + returnLimit + "/" + localLimit + "]" : "") + "  => " + matches.Count);
 					
 					// If we got back at least as many rows as our local (adaptive) limit,
 					// then we know that the limiting (possibly) had an effect and we may
@@ -676,5 +690,96 @@
 			}
 			return false;
 		}
+
+		public static Statement[] ReorderQuery(Statement[] graph, Variable[] fixedVariables, SelectableSource schema, bool[,] groupingPreference) {
+			// Find an optimal order of the statements in the graph to run the
+			// query in, assuming each statement is executed one at a time.
+			// This is a greedy algorithm: at each step, choose the statement
+			// that has the fewest unseen variables, or failing that, if we have
+			// schema information, the statement that is expected to have the
+			// fewest results based on functional and inverse functional property
+			// typing, and failing that if we have a preference for which statements
+			// to group together (because they will be answered by the same data source),
+			// then grouping that way.
+			
+			bool[] used = new bool[graph.Length];
+			int[] permutation = new int[graph.Length];
+			
+			ResSet selectedVars = new ResSet();
+			selectedVars.AddRange(fixedVariables);
+			
+			Entity rdf_type = "http://www.w3.org/1999/02/22-rdf-syntax-ns#type";;
+			Entity owl_inversefunctional = "http://www.w3.org/2002/07/owl#InverseFunctionalProperty";;
+			Entity owl_functional = "http://www.w3.org/2002/07/owl#FunctionalProperty";;
+			
+			for (int i = 0; i < graph.Length; i++) {
+				// Choose the best alternative.
+				int best_index = -1, best_score = -1;
+				
+				for (int j = 0; j < graph.Length; j++) {
+					if (used[j]) continue;
+					
+					// Compute a 'data complexity' for the graph statement.
+					int score = 1;
+					
+					// For each component of the graph statement...
+					for (int k = 0; k < 4; k++) {
+						Resource v = graph[j].GetComponent(k);
+						if (!(v is Variable)) continue;
+						if (selectedVars.Contains(v)) continue;
+						
+						int cscore = 3;
+						if (schema != null) {
+							// Don't penalize so much if the predicate is functional or inverse functional and
+							// the other side of the statement has already been selected.
+							Entity predicate = (Entity)graph[j].GetComponent(1);
+							if (k == 0 && selectedVars.Contains(graph[j].GetComponent(2)) && schema.Contains(new Statement(predicate, rdf_type, owl_inversefunctional)))
+								cscore = 2;
+							if (k == 2 && selectedVars.Contains(graph[j].GetComponent(0)) && schema.Contains(new Statement(predicate, rdf_type, owl_functional)))
+								cscore = 2;
+						}
+						
+						score *= cscore;
+					}
+					
+					//Console.WriteLine(j + ") " + score + " " + graph[j] + " " + ((i > 0 && groupingPreference != null
+					//	&& groupingPreference[permutation[i-1], j]) ? "GROUP" : ""));
+					
+					// If we found something with a better score, than use this statement next.
+					if (score < best_score || best_index == -1) {
+						best_score = score;
+						best_index = j;
+					}
+					
+					// If we found something with the same score, but we have a preference for
+					// grouping this statement with the previous, than use this statement next.
+					if (score == best_score && i > 0 && groupingPreference != null
+						&& !groupingPreference[permutation[i-1], best_index]
+						&& groupingPreference[permutation[i-1], j]) {
+						best_score = score;
+						best_index = j;
+					}
+				}
+				
+				// At this point we've picked the best statement to use next.
+				//Console.WriteLine("Chose " + best_index);
+				used[best_index] = true;
+				permutation[i] = best_index;
+				for (int k = 0; k < 4; k++) {
+					Resource v = graph[best_index].GetComponent(k);
+					if (!(v is Variable)) continue;
+					selectedVars.Add(v);
+				}
+			}
+			
+			// We've chosen a new permutation.
+			Statement[] neworder = new Statement[graph.Length];
+			for (int i = 0; i < graph.Length; i++) {
+				neworder[i] = graph[permutation[i]];
+			}
+			
+			return neworder;
+		}
+		
 	}
 }

Modified: trunk/beagle/Util/SemWeb/Inference.cs
==============================================================================
--- trunk/beagle/Util/SemWeb/Inference.cs	(original)
+++ trunk/beagle/Util/SemWeb/Inference.cs	Mon Jun  2 11:28:03 2008
@@ -197,6 +197,17 @@
 					ret.Append(step.Substitutions[v]);
 					ret.Append("\n");
 				}
+				if (vars.Count > 0) {
+					foreach (Statement s in step.Rule.Consequent) {
+						Statement ss = s;
+						foreach (Variable v in vars)
+							ss = ss.Replace(v, (Resource)step.Substitutions[v]);
+						
+						ret.Append("\t\t=> ");
+						ret.Append(ss.ToString());
+						ret.Append("\n");
+					}
+				}
 			}
 			
 			return ret.ToString();

Modified: trunk/beagle/Util/SemWeb/Interfaces.cs
==============================================================================
--- trunk/beagle/Util/SemWeb/Interfaces.cs	(original)
+++ trunk/beagle/Util/SemWeb/Interfaces.cs	Mon Jun  2 11:28:03 2008
@@ -1,6 +1,5 @@
 using System;
 using System.Collections;
-//using System.Data;
 
 using SemWeb.Util;
 
@@ -69,4 +68,4 @@
 	}
 
 	
-}
+}
\ No newline at end of file

Modified: trunk/beagle/Util/SemWeb/N3Reader.cs
==============================================================================
--- trunk/beagle/Util/SemWeb/N3Reader.cs	(original)
+++ trunk/beagle/Util/SemWeb/N3Reader.cs	Mon Jun  2 11:28:03 2008
@@ -14,6 +14,8 @@
 		Resource BaseResource = new Literal("@base");
 		
 		TextReader sourcestream;
+		bool closed;
+
 		NamespaceManager namespaces = new NamespaceManager();
 
 		Entity entRDFTYPE = "http://www.w3.org/1999/02/22-rdf-syntax-ns#type";;
@@ -34,6 +36,12 @@
 			BaseUri = "file:" + sourcefile + "#";
 		}
 
+		protected override void Dispose() {
+			if (!closed)
+				sourcestream.Close();
+			closed = true;
+		}
+
 		private struct ParseContext {
 			public MyReader source;
 			public StatementSink store;
@@ -60,6 +68,8 @@
 			context.meta = Meta;
 			
 			while (ReadStatement(context)) { }
+			
+			Dispose();
 		}
 		
 		private bool ReadStatement(ParseContext context) {
@@ -612,13 +622,13 @@
 			
 			if (str[0] == '?') {
 				string name = str.Substring(1);
-				Entity var = (Entity)context.variables[name];
-				if (var == null) {
-					var = new Variable(name);
-					AddVariable((Variable)var);
-					context.variables[name] = var;
+				Entity varb = (Entity)context.variables[name];
+				if (varb == null) {
+					varb = new Variable(name);
+					AddVariable((Variable)varb);
+					context.variables[name] = varb;
 				}
-				return var;
+				return varb;
 			}
 			
 			// QNAME
@@ -707,7 +717,18 @@
 			
 			// In Turtle, numbers are restricted to [0-9]+, and are datatyped xsd:integer.
 			double numval;
+			#if !SILVERLIGHT
 			if (double.TryParse(str, System.Globalization.NumberStyles.Any, null, out numval)) {
+			#else
+			bool ok = true;
+			numval = 0;
+			try {
+				numval = double.Parse(str);
+			} catch (Exception) {
+				ok = false;
+			}
+			if (ok) {
+			#endif
 				if (numval >= long.MinValue && numval <= long.MaxValue && numval == (double)(long)numval)
 					return new Literal(((long)numval).ToString(), null, NS.XMLSCHEMA + "integer");
 				else

Modified: trunk/beagle/Util/SemWeb/N3Writer.cs
==============================================================================
--- trunk/beagle/Util/SemWeb/N3Writer.cs	(original)
+++ trunk/beagle/Util/SemWeb/N3Writer.cs	Mon Jun  2 11:28:03 2008
@@ -59,9 +59,11 @@
 		}
 
 		private string Literal(Literal literal) {
+			#if !SILVERLIGHT
 			if (format == Formats.NTriples || literal.DataType == null) return literal.ToString();
 			if (literal.DataType == xsdInteger) return literal.ParseValue().ToString();
 			if (literal.DataType == xsdDouble && format == Formats.Notation3) return literal.ParseValue().ToString();
+			#endif
 			return literal.ToString();
 		}
 		

Modified: trunk/beagle/Util/SemWeb/Query.cs
==============================================================================
--- trunk/beagle/Util/SemWeb/Query.cs	(original)
+++ trunk/beagle/Util/SemWeb/Query.cs	Mon Jun  2 11:28:03 2008
@@ -149,7 +149,11 @@
 		
 		public virtual string MimeType {
 			get {
+				#if !SILVERLIGHT
 				return SparqlXmlQuerySink.MimeType;
+				#else
+				throw new NotSupportedException();
+				#endif
 			}
 			set {
 				throw new NotSupportedException();
@@ -157,7 +161,11 @@
 		}
 		
 		public virtual void Run(SelectableSource source, TextWriter output) {
+			#if !SILVERLIGHT
 			Run(source, new SparqlXmlQuerySink(output));
+			#else
+			throw new NotSupportedException();
+			#endif
 		}
 
 		public abstract void Run(SelectableSource source, QueryResultSink resultsink);
@@ -230,7 +238,11 @@
 		#endif
 	}
 	
-	public class VariableBindings {
+	public class VariableBindings : IComparable
+	#if DOTNET2
+	, IComparable<VariableBindings>
+	#endif
+	{
 		Variable[] vars;
 		Resource[] vals;
 
@@ -288,6 +300,25 @@
 			}
 			return ret;
 		}
+		
+		int IComparable.CompareTo(object other) {
+			return CompareTo((VariableBindings)other);
+		}
+		public int CompareTo(VariableBindings other) {
+			for (int i = 0; i < vars.Length; i++) {
+				Resource a = vals[i];
+				Resource b = other.vals[i];
+				if (a == null && b == null)
+					continue;
+				if (a == null)
+					return -1;
+				if (b == null)
+					return 1;
+				int c = a.CompareTo(b);
+				if (c != 0) return c;
+			}
+			return 0;			
+		}
 	}
 }
 

Copied: trunk/beagle/Util/SemWeb/README.txt (from r4763, /trunk/beagle/Util/SemWeb/README)
==============================================================================
--- /trunk/beagle/Util/SemWeb/README	(original)
+++ trunk/beagle/Util/SemWeb/README.txt	Mon Jun  2 11:28:03 2008
@@ -1,16 +1,125 @@
-Semantic Web Library for C#/.NET
-================================
+SemWeb: A Semantic Web Library for C#/.NET
+==========================================
 
-By Joshua Tauberer <tauberer for net>
+By Joshua Tauberer <http://razor.occams.info>
 
-http://taubz.for.net/code/semweb
-
-Copyright 2005 Joshua Tauberer.  This package is released
-under the terms of the Creative Commons Attribution License:
-
-	http://creativecommons.org/licenses/by/2.0/
-
-The license basically means you can copy, distribute and
-modify this package however you like, but you need to
-give me due credit.  I think that's fair.
+http://razor.occams.info/code/semweb
 
+USAGE
+-----
+
+SemWeb is a library for use in other C# and .NET applications on either
+Mono or Microsoft's .NET. The library comes as a collection of
+.NET assemblies. There are two directories for the compiled assemblies:
+
+	bin: Binaries compiled for .NET 1.1 (no generics).
+	bin_generics: Binaries compiled for .NET 2.0 (generics).
+	
+Each directory contains the files:
+
+	SemWeb.dll
+		This is the core library.
+
+	SemWeb.MySQLStore.dll, SemWeb.PostgreSQLStore.dll, SemWeb.SqliteStore.dll
+		Assemblies providing SQLStore implementations for
+		those RDBMSs. (details to be entered here later)
+	
+	SemWeb.Sparql.dll
+		An assembly providing the SPARQL engine class. It requires
+		the auxiliary assemblies listed next.
+	
+	IKVM.GNU.Classpath.dll, IKVM.Runtime.dll
+	sparql-core.dll
+		Auxiliary assemblies required for SPARQL.
+
+	rdfstorage.exe
+		A command-line tool for converting files between RDF formats
+		and loading RDF files into databases.
+
+	rdfquery.exe
+		A command-line tool for running SPARQL and simple graph
+		matching (in N3) queries against a data source.
+
+	euler.exe
+		A command-line tool for performing general rule-based
+		reasoning.
+		
+	Mono.GetOptions.dll
+		This library from Mono is a dependency of all of the command-line
+		tools listed above.
+	
+	.mdb files are debugging symbol files for Mono. Running
+	under MS .NET, they are useless. Running under Mono, they
+	are optional unless you want debugging info in stack traces.
+
+	To use any of the .dll assemblies, reference them in your
+	project, and make sure they and any of their dependencies
+	are findable at runtime (which in MS .NET is usually the
+	case if you just reference them).
+	
+
+DOCUMENTATION
+-------------
+
+For more information, view doc/index.html and the API documentation
+in apidocs/index.html.
+
+
+BUILD INSTRUCTIONS
+------------------
+
+Run make if you're in Linux.  Nothing complicated here.  You'll need
+Mono installed (and the MySQL/Connector and Sqlite Client DLLs for SQL 
+database support, optionally).  It'll build .NET 1.1 binaries to the
+bin directory and .NET 2.0 binaries with generics to the bin_generics
+directory.
+
+A MonoDevelop solution file (semweb.mds) and a Visual Studio 2005 solution
+file (SemWeb.sln) are included too.  They build .NET 2.0 binaries with
+generics to the bin_generics directory.
+
+If you build the MySQL and SQLite .cs files, you'll need to reference 
+MySQL's MySql.Data.dll and Sqlite Client assemblies (see www.mono-project.com).  Otherwise just leave out those .cs files.
+Put MySql.Data.dll in a "lib" directory within the SemWeb directory.
+
+The sources are set up with a conditional compilation flag "DOTNET2" so 
+that the sources can be compiled for both .NET 1.1 and 2.0, taking 
+advantage of generics.
+
+
+LICENSE
+-------
+
+The portions of this library not written by someone else are Copyright 2007
+Joshua Tauberer, and are dual-licensed under both the GNU GPL (version 2
+or later) and the Creative Commons Attribution license. See below for
+licensing information on the 3rd-party components of the library. Everything
+not listed below was written originally by me.
+
+In short, you can use the library if the program it is incorporated
+into either 1) is licensed under the GNU GPL, or 2) credits me for
+the use of the SemWeb library. However, this does not cover any obligations
+you may have for using the components of the library derived from
+other projects.
+
+
+IMPORTED FILES FROM OTHER PROJECTS & CREDITS
+--------------------------------------------
+
+sparql-core.dll is based on the SPARQL Engine by Ryan Levering,
+which is covered by the GNU LGPL.  The original Java JAR was
+coverted to a .NET assembly using IKVM (see below).  Actually, I've
+made numerous changes to the library so it can take advantage of
+faster API paths in SemWeb.
+See: http://sparql.sourceforge.net/
+
+IKVM*.dll are auxiliary assemblies for running the SPARQL
+engine.  IKVM was written by Jeroen Frijters.  See http://www.ikvm.net.
+The IVKM license is the zlib license, which is GPL compatible.
+
+Euler.cs is adapted from Jos De Roo's JavaScript Euler inferencing
+engine.  See: http://www.agfa.com/w3c/euler/ The original source
+code (and thus this derived file) was licensed under the W3C Software
+License, which is GPL compatible. The packaged SemWeb.dll contains
+this source code, and is thus (as is) released under the GPL license
+only (not the CC license).

Modified: trunk/beagle/Util/SemWeb/RdfReader.cs
==============================================================================
--- trunk/beagle/Util/SemWeb/RdfReader.cs	(original)
+++ trunk/beagle/Util/SemWeb/RdfReader.cs	Mon Jun  2 11:28:03 2008
@@ -71,7 +71,11 @@
 
 		public abstract void Select(StatementSink sink);
 		
-		public virtual void Dispose() {
+		protected virtual void Dispose() {
+		}
+		
+		void IDisposable.Dispose() {
+			Dispose();
 		}
 		
 		internal static string NormalizeMimeType(string type) {
@@ -118,7 +122,7 @@
 			}
 		}
 
-		/*
+		#if false
 		public static RdfReader LoadFromUri(Uri webresource) {
 			// TODO: Add Accept header for HTTP resources.
 			
@@ -152,7 +156,7 @@
 			
 			return reader;
 		}
-		*/
+		#endif
 		
 		internal static TextReader GetReader(string file) {
 			if (file == "-") return Console.In;
@@ -170,6 +174,7 @@
 				return uri;
 			}
 			if (uri.IndexOf(':') != -1) return uri;
+			#if !SILVERLIGHT
 			try {
 				UriBuilder b = new UriBuilder(baseuri);
 				b.Fragment = null; // per W3 RDF/XML test suite
@@ -177,6 +182,9 @@
 			} catch (UriFormatException) {
 				return baseuri + uri;
 			}			
+			#else
+			return baseuri + uri;
+			#endif
 		}
 
 	}

Modified: trunk/beagle/Util/SemWeb/RdfXmlReader.cs
==============================================================================
--- trunk/beagle/Util/SemWeb/RdfXmlReader.cs	(original)
+++ trunk/beagle/Util/SemWeb/RdfXmlReader.cs	Mon Jun  2 11:28:03 2008
@@ -11,6 +11,7 @@
 		// TODO: Make some of the errors warnings.
 	
 		XmlReader xml;
+		bool closed;
 		
 		Hashtable blankNodes = new Hashtable();
 		UriMap namedNodes = new UriMap();
@@ -36,14 +37,31 @@
 		#endif
 		
 		public RdfXmlReader(XmlReader document) {
-			XmlValidatingReader reader = new XmlValidatingReader(document); // decodes entity definitions
-			reader.ValidationType = ValidationType.None;
+			#if !DOTNET2
+				XmlValidatingReader reader = new XmlValidatingReader(document); // decodes entity definitions
+				reader.ValidationType = ValidationType.None;
+			#elif SILVERLIGHT
+				XmlReader reader = document;
+			#else
+				XmlReaderSettings settings = new XmlReaderSettings();
+				settings.ValidationType = ValidationType.None;
+				settings.ProhibitDtd = false;
+				settings.CloseInput = true;
+				XmlReader reader = XmlReader.Create(document, settings);
+			#endif
+        
 			xml = new XmlBaseAwareReader(reader);
 			LoadNamespaces();
 		}
 		
+		#if !SILVERLIGHT
 		public RdfXmlReader(TextReader document) : this(new XmlTextReader(document)) {
 		}
+		#else
+		public RdfXmlReader(TextReader document) {
+			throw new NotSupportedException("Reading RDF/XML from a TextReader is not supported in the Silverlight build of the SemWeb library. Someone needs to fix this.");
+		}
+		#endif
 
 		public RdfXmlReader(Stream document) : this(new StreamReader(document)) {
 		}
@@ -61,6 +79,12 @@
 		public RdfXmlReader(string file) : this(GetReader(file), "file:///" + file) {
 		}
 		
+		protected override void Dispose() {
+			if (!closed)
+				xml.Close();
+			closed = true;
+		}
+
 		private void LoadNamespaces() {
 			// Move to the document element and load any namespace
 			// declarations on the node.
@@ -114,8 +138,8 @@
 				}
 				break;
 			}
-
-			xml.Close();
+			
+			Dispose();
 		}
 		
 		private string CurNode() {
@@ -414,7 +438,11 @@
 				if (xml.IsEmptyElement) {
 					objct = new Literal("", null, datatype);
 				} else {
+					#if !DOTNET2
 					objct = new Literal(xml.ReadString(), null, datatype);
+					#else
+					objct = new Literal(xml.ReadElementContentAsString(), null, datatype);
+					#endif
 					if (xml.NodeType != XmlNodeType.EndElement)
 						OnError("XML markup may not appear in a datatyped literal property.");
 				}
@@ -596,26 +624,27 @@
 			public override bool MoveToElement () { return _reader.MoveToElement(); }
 			public override bool MoveToFirstAttribute () { return _reader.MoveToFirstAttribute(); }
 			public override bool MoveToNextAttribute () { return _reader.MoveToNextAttribute(); }
-			public override bool ReadAttributeValue () { return _reader.ReadAttributeValue(); }
-			public override string ReadElementString () { return _reader.ReadElementString(); }
-			public override string ReadElementString (string name) { return _reader.ReadElementString(name); }
-			public override string ReadElementString (string localName, string namespaceName) { return _reader.ReadElementString(localName, namespaceName); }
 			public override void ReadEndElement () { _reader.ReadEndElement(); }
 			public override string ReadInnerXml () { return _reader.ReadInnerXml(); }
 			public override string ReadOuterXml () { return _reader.ReadOuterXml(); }
 			public override void ReadStartElement () { _reader.ReadStartElement(); }
 			public override void ReadStartElement (string name) { _reader.ReadStartElement(name); }
 			public override void ReadStartElement (string localName, string namespaceName) { _reader.ReadStartElement(localName, namespaceName); }
+			public override void Skip () { _reader.Skip(); }
+
+			#if !SILVERLIGHT
+			public override bool ReadAttributeValue () { return _reader.ReadAttributeValue(); }
+			public override string ReadElementString () { return _reader.ReadElementString(); }
+			public override string ReadElementString (string name) { return _reader.ReadElementString(name); }
+			public override string ReadElementString (string localName, string namespaceName) { return _reader.ReadElementString(localName, namespaceName); }
 			public override string ReadString () { return _reader.ReadString(); }
 			public override void ResolveEntity () { _reader.ResolveEntity(); }
-			public override void Skip () { _reader.Skip(); }
+			#endif
 			
 			public override int AttributeCount { get { return _reader.AttributeCount; } }
-			public override bool CanResolveEntity { get { return _reader.CanResolveEntity; } }
 			public override int Depth { get { return _reader.Depth; } }
 			public override bool EOF { get { return _reader.EOF; } }
 			public override bool HasAttributes { get { return _reader.HasAttributes; } }
-			public override bool HasValue { get { return _reader.HasValue; } }
 			public override bool IsDefault { get { return _reader.IsDefault; } }
 			public override bool IsEmptyElement { get { return _reader.IsEmptyElement; } }
 			public override string this [int i] { get { return _reader[i]; } }
@@ -627,11 +656,16 @@
 			public override XmlNameTable NameTable { get { return _reader.NameTable; } }
 			public override XmlNodeType NodeType { get { return _reader.NodeType; } }
 			public override string Prefix { get { return _reader.Prefix; } }
-			public override char QuoteChar { get { return _reader.QuoteChar; } }
 			public override ReadState ReadState { get { return _reader.ReadState; } }
 			public override string Value { get { return _reader.Value; } }
 			public override string XmlLang { get { return _reader.XmlLang; } }
 			public override XmlSpace XmlSpace { get { return _reader.XmlSpace; } }
+			
+			#if !SILVERLIGHT
+			public override bool CanResolveEntity { get { return _reader.CanResolveEntity; } }
+			public override bool HasValue { get { return _reader.HasValue; } }
+			public override char QuoteChar { get { return _reader.QuoteChar; } }
+			#endif
 		}		
 	}
 }

Modified: trunk/beagle/Util/SemWeb/Resource.cs
==============================================================================
--- trunk/beagle/Util/SemWeb/Resource.cs	(original)
+++ trunk/beagle/Util/SemWeb/Resource.cs	Mon Jun  2 11:28:03 2008
@@ -4,8 +4,10 @@
 
 namespace SemWeb {
 	
-	public abstract class Resource :
-	IComparable
+	public abstract class Resource : IComparable
+#if DOTNET2
+	, IComparable<Resource>
+#endif
 	{
 		internal object ekKey, ekValue;
 		internal ArrayList extraKeys;
@@ -67,14 +69,17 @@
 			extraKeys.Add(k);
 		}
 
-		public int CompareTo(object other) {
+		int IComparable.CompareTo(object other) {
+			return CompareTo((Resource)other);
+		}
+		public int CompareTo(Resource other) {
 			// We'll make an ordering over resources.
 			// First named entities, then bnodes, then literals.
 			// Named entities are sorted by URI.
 			// Bnodes by hashcode.
 			// Literals by their value, language, datatype.
 		
-			Resource r = (Resource)other;
+			Resource r = other;
 			if (Uri != null && r.Uri == null) return -1;
 			if (Uri == null && r.Uri != null) return 1;
 			if (this is BNode && r is Literal) return -1;
@@ -510,6 +515,7 @@
 			return new Literal(value, lang, datatype);
 		}
 		
+		#if !SILVERLIGHT
 		public object ParseValue() {
 			string dt = DataType;
 			if (dt == null || !dt.StartsWith(NS.XMLSCHEMA)) return Value;
@@ -542,6 +548,7 @@
 			if (DataType == null) return this;
 			return new Literal(ParseValue().ToString(), Language, DataType);
 		}
+		#endif
 		
 		public static Literal FromValue(float value) {
 			return new Literal(value.ToString(), null, NS.XMLSCHEMA + "float");

Modified: trunk/beagle/Util/SemWeb/SQLStore.cs
==============================================================================
--- trunk/beagle/Util/SemWeb/SQLStore.cs	(original)
+++ trunk/beagle/Util/SemWeb/SQLStore.cs	Mon Jun  2 11:28:03 2008
@@ -44,6 +44,8 @@
  * not in this table or in the _literals table.
  */
 
+#if !SILVERLIGHT
+
 using System;
 using System.Collections;
 using System.Collections.Specialized;
@@ -124,7 +126,6 @@
 		// Debugging flags from environment variables.
 		static bool Debug = System.Environment.GetEnvironmentVariable("SEMWEB_DEBUG_SQL") != null;
 		static bool DebugLogSpeed = System.Environment.GetEnvironmentVariable("SEMWEB_DEBUG_SQL_LOG_SPEED") != null;
-		static bool NoSQLView = System.Environment.GetEnvironmentVariable("SEMWEB_SQL_NOVIEWS") != null;
 		static string InitCommands = System.Environment.GetEnvironmentVariable("SEMWEB_SQL_INIT_COMMANDS");
 		
 		// This guy is reused in various calls to avoid allocating a new one of
@@ -183,6 +184,7 @@
 		protected abstract string InsertIgnoreCommand { get; }
 		protected abstract bool SupportsInsertCombined { get; }
 		protected abstract bool SupportsSubquery { get; }
+		protected virtual bool SupportsLimitClause { get { return true; } }
 		protected virtual bool SupportsViews { get { return false; } }
 		protected virtual int MaximumUriLength { get { return -1; } }
 		
@@ -368,13 +370,18 @@
 				if (ret != null) return (int)ret;
 			} else {
 				StringBuilder b = cmdBuffer; cmdBuffer.Length = 0;
-				b.Append("SELECT id FROM ");
+				b.Append("SELECT ");
+				if (!SupportsLimitClause)
+					b.Append("TOP 1 ");
+				b.Append("id FROM ");
 				b.Append(table);
 				b.Append("_literals WHERE hash =");
 				b.Append(quote);
 				b.Append(GetLiteralHash(literal));
 				b.Append(quote);
-				b.Append(" LIMIT 1;");
+				if (SupportsLimitClause)
+					b.Append(" LIMIT 1");
+				b.Append(';');
 				
 				object id = RunScalar(b.ToString());
 				if (id != null) return AsInt(id);
@@ -1139,6 +1146,7 @@
 		void PrefetchResourceIds(IList resources) {
 			Hashtable seen_e = new Hashtable();
 			Hashtable seen_l = new Hashtable();
+			Hashtable res_map = new Hashtable();
 			
 			int resStart = 0;
 			while (resStart < resources.Count) {
@@ -1165,7 +1173,14 @@
 				ctr++;
 			
 				if (r.Uri != null) {
-					if (seen_e.ContainsKey(r.Uri)) continue;
+					if (seen_e.ContainsKey(r.Uri)) {
+						// We can only query for a URI once, but it might be that multiple objects
+						// coming in have the same URI, so when we see a duplicate, associate the
+						// duplicate with the first instance of the URI we saw.
+						if ((object)r != (object)seen_e[r.Uri])
+							res_map[r] = seen_e[r.Uri];
+						continue;
+					}
 					if (hasEnts)
 						cmd_e.Append(" , ");
 					EscapedAppend(cmd_e, r.Uri);
@@ -1176,7 +1191,14 @@
 				Literal lit = r as Literal;
 				if (lit != null) {
 					string hash = GetLiteralHash(lit);
-					if (seen_l.ContainsKey(hash)) continue;
+					if (seen_l.ContainsKey(hash)) {
+						// We can only query for a literal value once, but it might be that multiple objects
+						// coming in have the same value, so when we see a duplicate, associate the
+						// duplicate with the first instance of the value we saw.
+						if ((object)lit != (object)seen_l[hash])
+							res_map[lit] = seen_l[hash];
+						continue;
+					}
 					
 					if (hasLiterals)
 						cmd_l.Append(" , ");
@@ -1211,6 +1233,10 @@
 			}
 			
 			}
+			
+			foreach (Resource r in res_map.Keys) {
+				SetResourceKey(r, GetResourceKey((Resource)res_map[r]));
+			}
 		}
 		
 		public void Select(Statement template, StatementSink result) {
@@ -1253,8 +1279,11 @@
 			}*/
 			
 			// Have to select something
-			if (!columns.SubjectId && !columns.PredicateId && !columns.ObjectId && !columns.MetaId)
+			bool fakeSubjectIdSelect = false;
+			if (!columns.SubjectId && !columns.PredicateId && !columns.ObjectId && !columns.MetaId) {
 				columns.SubjectId = true;
+				fakeSubjectIdSelect = true;
+			}
 				
 			// Pre-cache the IDs of resources in a MultiRes. TODO: Pool these into one array.
 			foreach (Resource r in new Resource[] { templateSubject, templatePredicate, templateObject, templateMeta }) {
@@ -1272,6 +1301,11 @@
 			// exclude the results of the join.
 						
 			System.Text.StringBuilder cmd = new System.Text.StringBuilder("SELECT ");
+			if (!SupportsLimitClause && limit >= 1) {
+				cmd.Append("TOP ");
+				cmd.Append(limit);
+				cmd.Append(' ');
+			}
 			if (!HasUniqueStatementsConstraint)
 				cmd.Append("DISTINCT ");
 			SelectFilterColumns(columns, cmd);
@@ -1297,7 +1331,7 @@
 				}
 			}
 			
-			if (limit >= 1) {
+			if (SupportsLimitClause && limit >= 1) {
 				cmd.Append(" LIMIT ");
 				cmd.Append(limit);
 			}
@@ -1337,7 +1371,7 @@
 					if (columns.ObjectData) { ot = reader.GetInt32(col++); ouri = AsString(reader[col++]); lv = AsString(reader[col++]); ll = AsString(reader[col++]); ld = AsString(reader[col++]);}
 					if (columns.MetaUri) { muri = AsString(reader[col++]); }
 					
-					Entity subject = GetSelectedEntity(sid, suri, templateSubject, columns.SubjectId, columns.SubjectUri, entMap);
+					Entity subject = GetSelectedEntity(sid, suri, templateSubject, columns.SubjectId && !fakeSubjectIdSelect, columns.SubjectUri, entMap);
 					Entity predicate = GetSelectedEntity(pid, puri, templatePredicate, columns.PredicateId, columns.PredicateUri, entMap);
 					Resource objec = GetSelectedResource(oid, ot, ouri, lv, ll, ld, templateObject, columns.ObjectId, columns.ObjectData, entMap);
 					Entity meta = GetSelectedEntity(mid, muri, templateMeta, columns.MetaId, columns.MetaUri, templateMeta != null ? entMap : null);
@@ -1359,7 +1393,48 @@
 		
 		public void Query(Statement[] graph, SemWeb.Query.QueryOptions options, SemWeb.Query.QueryResultSink sink) {
 			if (graph.Length == 0) throw new ArgumentException("graph array must have at least one element");
-
+			
+			// This method translates the graph pattern into a single SQL statement. Each graph statement
+			// corresponds to a new use of the _statements table in the FROM clause. For instance:
+			//     ?a foaf:knows ?b . ?b foaf:name ?c .
+			// translates to
+			//     SELECT
+			//       g0.subject, v0.value,
+			//       g0.object, v1.value,
+			//       g1.object, v2.value, v2lit.value, v2lit.language, v2lit.datatype
+			//     FROM
+			//       db_tables as g0 LEFT JOIN db_entities AS v0 ON g0.subject=v0.id LEFT JOIN db_entities AS v1 ON g0.object=v1.id,
+			//       db_tables as g1 LEFT JOIN db_entities AS v2 ON g1.object=v2.id LEFT JOIN db_literals AS v2lit ON g1.object=v2lit.id
+			//     WHERE
+			//       g0.predicate = <the id of the foaf:knows entity> AND
+			//       g1.predicate = <the id of the foaf:name entity> AND
+			//       g0.object = g1.subject
+			//
+			// If any variable column is an *undistinguished* variable --- which is to say that the caller
+			// says it is a variable, but is not concerned with its values --- then we want to apply
+			// DISTINCT to the SELECT statement. This is because while in the normal case we may get
+			// duplicates, we expect that to not occur more than the caller expects, but in the latter
+			// case there will often be many duplicates. Consider the SPARQL query:
+			//      SELECT DISTINCT ?p WHERE { ?s ?p ?o }
+			// to get a list of predicates in the dataset, which corresponds to the graph query
+			//      ?s ?p ?o
+			// where only ?p is distinguished.
+			// This normally translates to:
+			//     SELECT
+			//       g0.predicate, v0.value,
+			//     FROM
+			//       db_tables as g0 LEFT JOIN db_entities AS v0 ON g0.predicate=v0.id
+			// which of course is going to return a result for every triple in the database.
+			// So we add DISTINCT to beginning ("SELECT DISTINCT").
+			// Unfortunately, MySQL performs the DISTINCT bit only after the LEFT JOINs (which makes sense normally).
+			// That means that MySQL is repeatedly fetching the URI values of the predicates and checking
+			// if a new unique row has been created, and this is very slow. What we want is to get the distinct
+			// IDs of the predicates first, and then get their URIs.
+			// I first tried implementing this with VIEWs, but it didn't always speed things up, and it was
+			// difficult to manage the creation and deletion of VIEWs.
+			// So instead, in this case, we do the query in two parts. First we get the IDs of the variables,
+			// and then we get their URIs.
+			
 			options = options.Clone(); // because we modify the knownvalues array
 			
 			// Order the variables mentioned in the graph.
@@ -1406,8 +1481,6 @@
 					varOrder[ctr++] = v;
 			}
 			
-			bool useView = useDistinct && SupportsViews && !NoSQLView;
-			
 			// Set the initial bindings to the result sink
 
 			sink.Init(varOrder);
@@ -1474,8 +1547,12 @@
 			
 			string[] colnames = { "subject", "predicate", "object", "meta" };
 			
+			// we initialize these things while locked, but use them after we release the lock
+			ArrayList results = new ArrayList();
+			Hashtable resourceCache = new Hashtable(); // map resource ID to Resource instances
+						
 			// Lock the store and make sure we are initialized and any pending add's have been committed. 
-					
+
 			lock (syncroot) {
 			
 			Init();
@@ -1483,15 +1560,14 @@
 			
 			// Compile the SQL statement.
 
-			Hashtable varRef_Inner = new Hashtable(); // the column name representing the variable: if we're using VIEWs, then within the VIEW (i.e. name of column in underlying table)
-			Hashtable varRef_Outer = new Hashtable(); // if we're using VIEWs, then the column name representing the variable of the VIEW itself 
-			Hashtable varRef2 = new Hashtable();
-			Hashtable varSelectedLiteral = new Hashtable();
+			Hashtable varRef = new Hashtable(); // the column name representing the variable, as in "g0.subject"
+			Hashtable varRef2 = new Hashtable(); // the index of the variable, for accessing the entities and literals joined tables
+			Hashtable varSelectedLiteral = new Hashtable(); // whether the variable is in a literal column and a LEFT JOIN for the literals table was used for it
+			Hashtable varCouldBeLiteral = new Hashtable(); // whether the variable is only in literal columns
+			Hashtable varSelectedEntity = new Hashtable(); // whether a LEFT JOIN for the entities table was used for a variable
 			
 			StringBuilder fromClause = new StringBuilder();
 			StringBuilder whereClause = new StringBuilder();
-			StringBuilder outerSelectJoins = new StringBuilder();
-			StringBuilder outerWhereClause = new StringBuilder();
 			
 			for (int f = 0; f < graph.Length; f++) {
 				// For each filter, we select FROM the statements table with an
@@ -1505,6 +1581,7 @@
 				// For each component of the filter...
 				
 				for (int i = 0; i < 4; i++) {
+					// This has the name of the column corresponding to this variable (i.e. "g1.predicate").
 					string myRef = "g" + f + "." + colnames[i];
 					
 					Variable v = graph[f].GetComponent(i) as Variable;
@@ -1520,47 +1597,56 @@
 						// that the proper columns here and in a previous
 						// filter are forced to have the same value.
 					
-						if (!varRef_Inner.ContainsKey(v)) {
-							varRef_Inner[v] = myRef;
-							varRef_Outer[v] = "v" + Array.IndexOf(varOrder, v);
+						if (!varRef.ContainsKey(v)) {
+							// This is the first time we are seeing this variable.
+									
+							// Record the column name for the variable (i.e. g0.subject).
+							varRef[v] = myRef;
 							
-							int vIndex = varRef_Inner.Count;
+							// Record an index for the variable (i.e. 0, 1, 2, ...)
+							int vIndex = varRef.Count;
 							varRef2[v] = vIndex;
 							
+							varCouldBeLiteral[v] = (i == 2);
+							
+							// LEFT JOIN the entities table for this variable to get its URI
+							// only if it is a distinguished variable and we are not using DISTINCT.
+							varSelectedEntity[v] = false;
+							if (!useDistinct && distinguishedVars.Contains(v)) {
+								varSelectedEntity[v] = true; // Record that we are selecting the entities table for this variable.
+								fromClause.Append(" LEFT JOIN ");
+								fromClause.Append(table);
+								fromClause.Append("_entities AS vent");
+								fromClause.Append(vIndex);
+								fromClause.Append(" ON ");
+								fromClause.Append(myRef);
+								fromClause.Append("=");
+								fromClause.Append("vent" + vIndex + ".id ");
+							}
+									
+							// LEFT JOIN the literals table for this variable:
+							//    if it is in an object position
+							//    to get its value, language, and datatype only if it is a distinguished variable and we are not using DISTINCT
+							//    to apply a literal value filter (which will be done later)
 							#if !DOTNET2
 							bool hasLitFilter = (options.VariableLiteralFilters != null && options.VariableLiteralFilters[v] != null);
 							#else
 							bool hasLitFilter = (options.VariableLiteralFilters != null && options.VariableLiteralFilters.ContainsKey(v));
 							#endif
-							if (distinguishedVars.Contains(v) || hasLitFilter) {
-								StringBuilder joinTarget = fromClause;
-								if (useView) joinTarget = outerSelectJoins;
-								
-								string onRef = (string)(!useView ? varRef_Inner : varRef_Outer)[v];
-								
-								joinTarget.Append(" LEFT JOIN ");
-								joinTarget.Append(table);
-								joinTarget.Append("_entities AS vent");
-								joinTarget.Append(vIndex);
-								joinTarget.Append(" ON ");
-								joinTarget.Append(onRef);
-								joinTarget.Append("=");
-								joinTarget.Append("vent" + vIndex + ".id ");
-								
-								varSelectedLiteral[v] = (i == 2);
-								
-								if (i == 2) { // literals cannot be in any other column
-									joinTarget.Append(" LEFT JOIN ");
-									joinTarget.Append(table);
-									joinTarget.Append("_literals AS vlit");
-									joinTarget.Append(vIndex);
-									joinTarget.Append(" ON ");
-									joinTarget.Append(onRef);
-									joinTarget.Append("=");
-									joinTarget.Append("vlit" + vIndex + ".id ");
-								}
+							varSelectedLiteral[v] = false;
+							if (i == 2 && ((!useDistinct && distinguishedVars.Contains(v)) || hasLitFilter)) {
+								varSelectedLiteral[v] = true; // Record that we are selecting the literals table for this variable.
+								fromClause.Append(" LEFT JOIN ");
+								fromClause.Append(table);
+								fromClause.Append("_literals AS vlit");
+								fromClause.Append(vIndex);
+								fromClause.Append(" ON ");
+								fromClause.Append(myRef);
+								fromClause.Append("=");
+								fromClause.Append("vlit" + vIndex + ".id ");
 							}
 							
+							// If this variable has known values, then we must restrict what values can appear using a WHERE clause.
 							if (options.VariableKnownValues != null) {
 								ICollection values = null;
 								#if DOTNET2
@@ -1582,17 +1668,24 @@
 							}
 							
 						} else {
+							// We've seen this variable before, so link up the column in this
+							// statement to the corresponding column in a previous (or this) statement.
 							if (whereClause.Length != 0) whereClause.Append(" AND ");
 							whereClause.Append('(');
-							whereClause.Append((string)varRef_Inner[v]);
+							whereClause.Append((string)varRef[v]);
 							whereClause.Append('=');
 							whereClause.Append(myRef);
 							whereClause.Append(')');
+							if (i != 2)
+								varCouldBeLiteral[v] = false;
 						}
 					
 					} else {
 						// If this is not a variable, then it is a resource.
 					
+						// Append something into the WHERE clause to make sure this component gets
+						// the right fixed value. If we cannot add the component to the WHERE clause
+						// because the fixed value isn't even known in the data source, we can stop early.
 						if (!WhereItem(myRef, graph[f].GetComponent(i), whereClause, whereClause.Length != 0)) {
 							// We know at this point that the query cannot return any results.
 							sink.Finished();
@@ -1623,52 +1716,39 @@
 					string s = FilterToSQL(filter, "vlit" + (int)varRef2[v] + ".value");
 					if (s == null) continue;
 
-					StringBuilder where = whereClause;
-					if (useView) where = outerWhereClause;
-					
-					if (where.Length != 0) where.Append(" AND ");
-					where.Append(s);
+					if (whereClause.Length != 0) whereClause.Append(" AND ");
+					whereClause.Append(s);
 				}
 			}
 
 			// Put the parts of the SQL statement together
 
 			StringBuilder cmd = new StringBuilder();
-			StringBuilder outercmd = new StringBuilder();
 			
-			string viewname = "queryview" + Math.Abs(GetHashCode());
-			if (useView) {
-				cmd.Append("DROP VIEW IF EXISTS ");
-				cmd.Append(viewname);
-				cmd.Append("; CREATE VIEW ");
-				cmd.Append(viewname);
-				cmd.Append(" AS ");
-				
-				outercmd.Append("SELECT ");
-			}
 			cmd.Append("SELECT ");
+
+			if (!SupportsLimitClause && options.Limit > 0) {
+				cmd.Append("TOP ");
+				cmd.Append(options.Limit);
+				cmd.Append(' ');
+			}
 			
 			if (useDistinct) cmd.Append("DISTINCT ");
 			
-			for (int i = 0; i < varOrder.Length; i++) {
-				if (i > 0) cmd.Append(',');
-				cmd.Append((string)varRef_Inner[varOrder[i]]);
+			// Add all of the distinguished variables to the SELECT clause.
+			bool firstvar = true;
+			foreach (Variable v in varOrder) {
+				if (!firstvar) cmd.Append(','); firstvar = false;
+				
+				cmd.Append((string)varRef[v]);
 				
-				StringBuilder c = cmd;
-				if (useView) {
-					cmd.Append(" AS ");
-					cmd.Append((string)varRef_Outer[varOrder[i]]);
-
-					if (i > 0) outercmd.Append(',');
-					outercmd.Append((string)varRef_Outer[varOrder[i]]);
-					c = outercmd;
-				}
-				
-				c.Append(", vent" + (int)varRef2[varOrder[i]] + ".value");
-				if ((bool)varSelectedLiteral[varOrder[i]]) {
-					c.Append(", vlit" + (int)varRef2[varOrder[i]] + ".value");
-					c.Append(", vlit" + (int)varRef2[varOrder[i]] + ".language");
-					c.Append(", vlit" + (int)varRef2[varOrder[i]] + ".datatype");
+				if ((bool)varSelectedEntity[v]) {
+					cmd.Append(", vent" + (int)varRef2[v] + ".value");
+				}
+				if ((bool)varSelectedLiteral[v]) {
+					cmd.Append(", vlit" + (int)varRef2[v] + ".value");
+					cmd.Append(", vlit" + (int)varRef2[v] + ".language");
+					cmd.Append(", vlit" + (int)varRef2[v] + ".datatype");
 				}
 			}
 			
@@ -1679,83 +1759,170 @@
 				cmd.Append(" WHERE ");
 			cmd.Append(whereClause.ToString());
 			
-			if (options.Limit > 0) {
+			if (SupportsLimitClause && options.Limit > 0) {
 				cmd.Append(" LIMIT ");
 				cmd.Append(options.Limit);
 			}
 			
 			cmd.Append(';');
 
-			if (useView) {
-				outercmd.Append(" FROM ");
-				outercmd.Append(viewname);
-				outercmd.Append(outerSelectJoins);
-				
-				if (outerWhereClause.Length > 0)
-					outercmd.Append(" WHERE ");
-				outercmd.Append(outerWhereClause.ToString());
-			}
-			
-			
 			if (Debug) {
 				string cmd2 = cmd.ToString();
 				//if (cmd2.Length > 80) cmd2 = cmd2.Substring(0, 80);
 				Console.Error.WriteLine(cmd2);
-				if (useView)
-					Console.Error.WriteLine(outercmd.ToString());
-			}
-			
-			// Execute the query
-			
-			Hashtable entityCache = new Hashtable();
-			
-			if (useView) {
-				RunCommand(cmd.ToString());
-				cmd = outercmd;
 			}
 			
-			try {
+			// Execute the query.
+					
+			// When we use DISTINCT and don't select URI and literal values at first,
+			// we have to select them after. And since we can't maintain two IDataReaders
+			// simultaneously, that means we have to pull the first set of results into
+			// memory. It would be nice to not have to do that when we don't use DISTINCT,
+			// but in practice it doesn't really matter since in SPARQL it's all sucked
+			// into memory anyway.
+					
 			using (IDataReader reader = RunReader(cmd.ToString())) {
 				while (reader.Read()) {
-					Resource[] variableBindings = new Resource[varOrder.Length];
-				
+					QueryResultRowVariable[] row = new QueryResultRowVariable[varOrder.Length];
+					results.Add(row);
+					
 					int col = 0;
 					for (int i = 0; i < varOrder.Length; i++) {
-						int id = reader.GetInt32(col++);
-						string uri = AsString(reader[col++]);
+						Variable v = varOrder[i];
 						
-						string litvalue = null, litlanguage = null, litdatatype = null;
-
-						if ((bool)varSelectedLiteral[varOrder[i]]) {
-							litvalue = AsString(reader[col++]);
-							litlanguage = AsString(reader[col++]);
-							litdatatype = AsString(reader[col++]);
+						row[i].id = reader.GetInt32(col++);
+						if ((bool)varSelectedEntity[v]) {
+							row[i].uri = AsString(reader[col++]);
 						}
-						
-						if (litvalue != null) {
-							Literal lit = new Literal(litvalue, litlanguage, litdatatype);
-							variableBindings[i] = lit;
-							
-							ArrayList litFilters = (ArrayList)varLitFilters[varOrder[i]];
-							if (litFilters != null && !LiteralFilter.MatchesFilters(lit, (LiteralFilter[])litFilters.ToArray(typeof(LiteralFilter)), this))
-								continue;
-							
-						} else {
-							variableBindings[i] = MakeEntity(id, uri, entityCache);
+						if ((bool)varSelectedLiteral[v]) {
+							row[i].litvalue = AsString(reader[col++]);
+							row[i].litlanguage = AsString(reader[col++]);
+							row[i].litdatatype = AsString(reader[col++]);
+						}
+					}
+				}
+			}
+		
+			// For any distinguished variable that we did not select URIs or literal values for,
+			// select that information now.
+			
+			for (int i = 0; i < varOrder.Length; i++) {
+				Variable v = varOrder[i];
+			
+				if ((bool)varSelectedEntity[v] && (!(bool)varCouldBeLiteral[v] || (bool)varSelectedLiteral[v])) continue;
+				
+				// Get the list of resource IDs found for this variable.
+				ArrayList rids = new ArrayList();
+				foreach (QueryResultRowVariable[] row in results) {
+					if (row[i].id <= 1) continue; // can't fetch for Statement.DefaultMeta
+					if (resourceCache.ContainsKey(row[i].id)) continue; // we've already fetched it
+					rids.Add(row[i].id); // probably no need to remove duplicates
+				}
+				
+				if (rids.Count > 0) {
+					// Fetch what we can for entities.
+					if (!(bool)varSelectedEntity[v]) {
+						StringBuilder cmd2 = new StringBuilder();
+						cmd2.Append("SELECT id, value FROM ");
+						cmd2.Append(table);
+						cmd2.Append("_entities WHERE id IN (");
+						bool first = true;
+						foreach (int id in rids) {
+							if (!first) cmd2.Append(','); first = false;
+							cmd2.Append(id);
+						}
+						cmd2.Append(")");
+						if (Debug) { Console.Error.WriteLine(cmd2.ToString()); }
+						using (IDataReader reader = RunReader(cmd2.ToString())) {
+							while (reader.Read()) {
+								int id = reader.GetInt32(0);
+								string uri = AsString(reader[1]);
+								resourceCache[id] = MakeEntity(id, uri, null);
+							}
+						}
+					}
+					
+					// Fetch what we can for literals.
+					if ((bool)varCouldBeLiteral[v] && !(bool)varSelectedLiteral[v]) {
+						StringBuilder cmd2 = new StringBuilder();
+						cmd2.Append("SELECT id, value, language, datatype FROM ");
+						cmd2.Append(table);
+						cmd2.Append("_literals WHERE id IN (");
+						bool first = true;
+						foreach (int id in rids) {
+							if (!first) cmd2.Append(','); first = false;
+							cmd2.Append(id);
+						}
+						cmd2.Append(")");
+						if (Debug) { Console.Error.WriteLine(cmd2.ToString()); }
+						using (IDataReader reader = RunReader(cmd2.ToString())) {
+							while (reader.Read()) {
+								int id = reader.GetInt32(0);
+								string value = AsString(reader[1]);
+								string language = AsString(reader[2]);
+								string datatype = AsString(reader[3]);
+								Literal lit = new Literal(value, language, datatype);
+								SetResourceKey(lit, new ResourceKey(id));
+								resourceCache[id] = lit;
+							}
 						}
 					}
 					
-					if (!sink.Add(new SemWeb.Query.VariableBindings(varOrder, variableBindings))) return;
+					// Any ids not found so far are bnodes.
+					foreach (int id in rids) {
+						if (!resourceCache.ContainsKey(id)) {
+							BNode b = new BNode();
+							SetResourceKey(b, new ResourceKey(id));
+							resourceCache[id] = b;
+						}
+					}
 				}
 			}
-			} finally {
-				if (useView)
-					RunCommand("DROP VIEW " + viewname);
-
-				sink.Finished();
+			
+			} // lock
+			
+			// Now loop through the binding results.
+			
+			foreach (QueryResultRowVariable[] row in results) {
+				bool match = true;
+				Resource[] variableBindings = new Resource[varOrder.Length];
+				
+				for (int i = 0; i < varOrder.Length; i++) {
+					int id = row[i].id;
+					if (resourceCache.ContainsKey(id)) {
+						variableBindings[i] = (Resource)resourceCache[id];
+					} else {
+						if (row[i].litvalue == null) {
+							variableBindings[i] = MakeEntity(id, row[i].uri, null);
+						} else {
+							Literal lit = new Literal(row[i].litvalue, row[i].litlanguage, row[i].litdatatype);
+							
+							ArrayList litFilters = (ArrayList)varLitFilters[varOrder[i]];
+							if (litFilters != null && !LiteralFilter.MatchesFilters(lit, (LiteralFilter[])litFilters.ToArray(typeof(LiteralFilter)), this)) {
+								match = false;
+								break;
+							}
+								
+							SetResourceKey(lit, new ResourceKey(id));
+							variableBindings[i] = lit;
+						}
+						
+						// reuse this entity later
+						resourceCache[id] = variableBindings[i];
+					}
+				}
+				
+				if (!match) continue;
+				if (!sink.Add(new SemWeb.Query.VariableBindings(varOrder, variableBindings))) return;
 			}
+			
+			sink.Finished();
+		}
 				
-			} // lock
+		private struct QueryResultRowVariable {
+			public int id;
+			public string uri;
+			public string litvalue, litlanguage, litdatatype;
 		}
 		
 		Entity GetSelectedEntity(int id, string uri, Resource given, bool idSelected, bool uriSelected, Hashtable entMap) {
@@ -1773,10 +1940,13 @@
 		Resource GetSelectedResource(int id, int type, string uri, string lv, string ll, string ld, Resource given, bool idSelected, bool uriSelected, Hashtable entMap) {
 			if (!idSelected) return (Resource)given;
 			if (!uriSelected) return (Resource)entMap[id];
-			if (type == 0)
+			if (type == 0) {
 				return MakeEntity(id, uri, entMap);
-			else
-				return new Literal(lv, ll, ld);
+			} else {
+				Literal lit = new Literal(lv, ll, ld);
+				SetResourceKey(lit, new ResourceKey(id));
+				return lit;
+			}
 		}
 		
 		private string CreateLikeTest(string column, string match, int method) {
@@ -1844,6 +2014,7 @@
 					case '\\':
 					case '\"':
 					case '*':
+					case '\'':
 						b.Append('\\');
 						b.Append(c);
 						break;
@@ -2047,3 +2218,5 @@
 	}
 	
 }
+
+#endif

Modified: trunk/beagle/Util/SemWeb/SparqlClient.cs
==============================================================================
--- trunk/beagle/Util/SemWeb/SparqlClient.cs	(original)
+++ trunk/beagle/Util/SemWeb/SparqlClient.cs	Mon Jun  2 11:28:03 2008
@@ -5,7 +5,6 @@
 #endif
 using System.IO;
 using System.Text;
-using System.Web;
 using System.Xml;
  
 using SemWeb;
@@ -21,7 +20,7 @@
 		void RunSparqlQuery(string sparqlQuery, QueryResultSink selectResults);
 	}
 
-#if false
+	#if false
 	public class SparqlHttpSource : QueryableSource, SparqlSource {
 		static bool Debug = System.Environment.GetEnvironmentVariable("SEMWEB_DEBUG_HTTP") != null;
 	
@@ -585,7 +584,7 @@
 			}
 		}
 	}
-#endif
+	#endif
 }
 
 namespace SemWeb.Query {
@@ -681,5 +680,5 @@
 			output.Flush();
 		}
 	}
-
+	
 }	

Modified: trunk/beagle/Util/SemWeb/SpecialRelations.cs
==============================================================================
--- trunk/beagle/Util/SemWeb/SpecialRelations.cs	(original)
+++ trunk/beagle/Util/SemWeb/SpecialRelations.cs	Mon Jun  2 11:28:03 2008
@@ -15,6 +15,7 @@
 	}
 	
 	namespace Relations {
+		#if !SILVERLIGHT
 		internal abstract class MathUnaryRelation : RdfRelation {
 			protected abstract Decimal EvaluateForward(Decimal left);
 			protected abstract Decimal EvaluateReverse(Decimal right);
@@ -249,6 +250,7 @@
 				return !(left == right);
 			}
 		}
+		#endif
 	}
 	
 }

Modified: trunk/beagle/Util/SemWeb/Store.cs
==============================================================================
--- trunk/beagle/Util/SemWeb/Store.cs	(original)
+++ trunk/beagle/Util/SemWeb/Store.cs	Mon Jun  2 11:28:03 2008
@@ -128,7 +128,8 @@
 				case "sqlite":
 				case "mysql":
 				case "postgresql":
-					if (spec == "") throw new ArgumentException("Use: sqlite|mysql|postgresql:table:connection-string");
+				case "sqlserver":
+					if (spec == "") throw new ArgumentException("Use: sqlite|mysql|postgresql|sqlserver:table:connection-string");
 				
 					c = spec.IndexOf(':');
 					if (c == -1) throw new ArgumentException("Invalid format for SQL spec parameter (table:constring).");
@@ -143,6 +144,8 @@
 						classtype = "SemWeb.Stores.MySQLStore, SemWeb.MySQLStore";
 					} else if (type == "postgresql") {
 						classtype = "SemWeb.Stores.PostgreSQLStore, SemWeb.PostgreSQLStore";
+					} else if( type == "sqlserver" ) {
+						classtype = "SemWeb.Stores.SQLServerStore, SemWeb.SQLServerStore";
 					}
 					ttype = Type.GetType(classtype);
 					if (ttype == null)
@@ -150,8 +153,12 @@
 					return Activator.CreateInstance(ttype, new object[] { spec, table });
 				/*case "bdb":
 					return new SemWeb.Stores.BDBStore(spec);*/
-				/*case "sparql-http":
-					return new SemWeb.Remote.SparqlHttpSource(spec);*/
+				case "sparql-http":
+					#if false
+					return new SemWeb.Remote.SparqlHttpSource(spec);
+					#else
+					throw new NotSupportedException("The SparqlHttpSource class is not available in the Silverlight build of SemWeb.");
+					#endif
 				case "class":
 					ttype = Type.GetType(spec);
 					if (ttype == null)
@@ -492,7 +499,7 @@
 
 		public void Query(Statement[] graph, SemWeb.Query.QueryOptions options, SemWeb.Query.QueryResultSink sink) {
 			// If reasoning is applied, delegate this call to the last reasoner
-			// and pass it a clone of this store but with itself removed.
+			// and pass it a clone of this store but with that reasoner removed.
 			ReasoningHelper rh = GetReasoningHelper(null);
 			if (rh != null) {
 				rh.reasoner.Query(graph, options, rh.nextStore, sink);
@@ -528,7 +535,58 @@
 				if (!mq[i].QuerySupported)
 					return null;
 			}
-		
+			
+			// Establish which statements can be answered definitively by which data sources.
+			bool[,] definitive = new bool[query.Length, allsources.Count];
+			for (int j = 0; j < query.Length; j++) {
+				// Find a definitive source for this statement
+				for (int i = 0; i < mq.Length; i++) {
+					if (mq[i].IsDefinitive != null && mq[i].IsDefinitive[j]) {
+						definitive[j,i] = true;
+						sink.AddComments("Data source '" + allsources[i] + "' definitively answers:  " + query[j]);
+					}
+				}
+				
+				// See if only one source can answer this statement.
+				System.Collections.ArrayList answerables = new System.Collections.ArrayList();
+				for (int i = 0; i < mq.Length; i++) {
+					if (mq[i].NoData != null && mq[i].NoData[j]) continue;
+					answerables.Add(i);
+				}
+				if (answerables.Count == 0) {
+					sink.AddComments("No data source could answer a part of the query: " + query[j]);
+					return null;
+				}
+				
+				if (answerables.Count == 1) {
+					//sink.AddComments("Only '" + allsources[(int)answerables[0]] + "' could answer:  " + query[j]);
+					definitive[j,(int)answerables[0]] = true;
+				}
+			}
+			
+			// Create a table that indicates preferred grouping: two statements that can be
+			// definitively answered by the same data source prefer to be grouped together.
+			bool[,] group = new bool[query.Length, query.Length];
+			for (int i = 0; i < query.Length; i++) {
+				for (int j = 0; j < query.Length; j++) {
+					if (i == j) continue;
+					
+					for (int k = 0; k < mq.Length; k++) {
+						if (definitive[i,k] && definitive[j,k]) {
+							group[i,j] = true;
+							group[j,i] = true;
+						}
+					}
+				}
+			}
+			
+			// Reorder the statements. Then run MetaQuery again because the order of statements changed.
+			query = SemWeb.Query.GraphMatch.ReorderQuery(query, SemWeb.Query.GraphMatch.toArray(options.VariableKnownValues), this, group);
+			for (int i = 0; i < allsources.Count; i++)
+				mq[i] = ((QueryableSource)allsources[i]).MetaQuery(query, options);
+
+			// Chunk the statements.
+			
 			System.Collections.ArrayList chunks = new System.Collections.ArrayList();
 			
 			int curSource = -1;
@@ -540,7 +598,6 @@
 					// statement in the graph, include this statement in the
 					// current chunk.
 					if (mq[curSource].IsDefinitive != null && mq[curSource].IsDefinitive[j]) {
-						sink.AddComments(allsources[curSource] + " answers definitively: " + query[j]);
 						curStatements.Add(query[j]);
 						continue;
 					}
@@ -576,7 +633,6 @@
 					if (mq[i].IsDefinitive != null && mq[i].IsDefinitive[j]) {
 						curSource = i;
 						curStatements.Add(query[j]);
-						sink.AddComments(allsources[i] + " answers definitively: " + query[j]);
 						break;
 					}
 				}
@@ -602,7 +658,6 @@
 					continue;
 				}
 				if (answerables.Count == 0) {
-					sink.AddComments("No data source could answer: " + query[j]);
 					return null;
 				}
 				
@@ -737,7 +792,7 @@
 		// ModifiableSource
 		
 		public void Clear() {
-			if (allsources.Count > 0) throw new InvalidOperationException("The Clear() method is not supported when multiple data sources are added to a Store.");
+			if (allsources.Count > 1) throw new InvalidOperationException("The Clear() method is not supported when multiple data sources are added to a Store.");
 			if (!(allsources[0] is ModifiableSource)) throw new InvalidOperationException("The data source is not modifiable.");
 			((ModifiableSource)allsources[0]).Clear();
 		}
@@ -1013,7 +1068,7 @@
 		}
 
 		public void Query(Statement[] graph, SemWeb.Query.QueryOptions options, SemWeb.Query.QueryResultSink sink) {
-			output.WriteLine("QUERY:");
+			output.WriteLine("QUERY: " + source);
 			foreach (Statement s in graph)
 				output.WriteLine("\t" + s);
 			if (options.VariableKnownValues != null) {

Modified: trunk/beagle/Util/SemWeb/UriMap.cs
==============================================================================
--- trunk/beagle/Util/SemWeb/UriMap.cs	(original)
+++ trunk/beagle/Util/SemWeb/UriMap.cs	Mon Jun  2 11:28:03 2008
@@ -145,7 +145,11 @@
 				}
 				
 				if (Children == null)
+					#if !SILVERLIGHT
 					Children = new HybridDictionary();
+					#else
+					Children = new Hashtable();
+					#endif
 				
 				Node ret = (Node)Children[name];
 				if (ret != null || !create) {

Modified: trunk/beagle/Util/SemWeb/Util.cs
==============================================================================
--- trunk/beagle/Util/SemWeb/Util.cs	(original)
+++ trunk/beagle/Util/SemWeb/Util.cs	Mon Jun  2 11:28:03 2008
@@ -218,8 +218,7 @@
 
 		public virtual void RemoveAt(int index) { 
 			if (index < 0 || index >= _size) 
-				throw new ArgumentOutOfRangeException("index", index,
-					"Less than 0 or more than list count.");
+				throw new ArgumentOutOfRangeException();
 			Shift(index, -1);
 			_size--;
 		}

Added: trunk/beagle/Util/SemWeb/upstream-change.diff
==============================================================================
--- (empty file)
+++ trunk/beagle/Util/SemWeb/upstream-change.diff	Mon Jun  2 11:28:03 2008
@@ -0,0 +1,50 @@
+Mostly to remove unneeded dependency on System.Remote.
+
+--- /usr/share/devel/cvsdev/semweb/src/Store.cs	2008-06-02 06:53:41.765078790 -0400
++++ Store.cs	2008-06-02 06:58:47.705070026 -0400
+@@ -152,7 +152,7 @@
+ 				/*case "bdb":
+ 					return new SemWeb.Stores.BDBStore(spec);*/
+ 				case "sparql-http":
+-					#if !SILVERLIGHT
++					#if false
+ 					return new SemWeb.Remote.SparqlHttpSource(spec);
+ 					#else
+ 					throw new NotSupportedException("The SparqlHttpSource class is not available in the Silverlight build of SemWeb.");
+--- /usr/share/devel/cvsdev/semweb/src/RdfReader.cs	2008-06-02 06:53:41.921075546 -0400
++++ RdfReader.cs	2008-06-02 06:59:20.221472537 -0400
+@@ -122,7 +122,7 @@
+ 			}
+ 		}
+ 
+-		#if !SILVERLIGHT
++		#if false
+ 		public static RdfReader LoadFromUri(Uri webresource) {
+ 			// TODO: Add Accept header for HTTP resources.
+ 			
+--- /usr/share/devel/cvsdev/semweb/src/SparqlClient.cs	2008-06-02 06:53:41.789077382 -0400
++++ SparqlClient.cs	2008-06-02 07:07:54.145078453 -0400
+@@ -20,7 +20,7 @@
+ 		void RunSparqlQuery(string sparqlQuery, QueryResultSink selectResults);
+ 	}
+ 
+-	#if !SILVERLIGHT
++	#if false
+ 	public class SparqlHttpSource : QueryableSource, SparqlSource {
+ 		static bool Debug = System.Environment.GetEnvironmentVariable("SEMWEB_DEBUG_HTTP") != null;
+ 	
+@@ -584,6 +584,7 @@
+ 			}
+ 		}
+ 	}
++	#endif
+ }
+ 
+ namespace SemWeb.Query {
+@@ -679,6 +680,5 @@
+ 			output.Flush();
+ 		}
+ 	}
+-	#endif
+ 	
+ }	



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