f-spot r3920 - in trunk: . semweb src



Author: sdelcroix
Date: Fri May 16 18:52:24 2008
New Revision: 3920
URL: http://svn.gnome.org/viewvc/f-spot?rev=3920&view=rev

Log:
2008-05-16  Stephane Delcroix  <stephane delcroix org>

	reverting the semweb to the old _but working_ one we had.


Added:
   trunk/semweb/RSquary.cs
   trunk/semweb/RSquaryFilters.cs
   trunk/semweb/Remote.cs
   trunk/semweb/XPathSemWebNavigator.cs
Removed:
   trunk/semweb/Euler.cs
   trunk/semweb/GraphMatch.cs
   trunk/semweb/Interfaces.cs
   trunk/semweb/SparqlClient.cs
   trunk/semweb/SpecialRelations.cs
Modified:
   trunk/ChangeLog
   trunk/Makefile.am
   trunk/Makefile.include
   trunk/configure.in
   trunk/semweb/Algos.cs
   trunk/semweb/AssemblyInfo.cs
   trunk/semweb/Inference.cs
   trunk/semweb/LiteralFilters.cs
   trunk/semweb/Makefile.am
   trunk/semweb/MemoryStore.cs
   trunk/semweb/N3Reader.cs
   trunk/semweb/N3Writer.cs
   trunk/semweb/NamespaceManager.cs
   trunk/semweb/Query.cs
   trunk/semweb/RDFS.cs
   trunk/semweb/README
   trunk/semweb/RdfReader.cs
   trunk/semweb/RdfWriter.cs
   trunk/semweb/RdfXmlReader.cs
   trunk/semweb/RdfXmlWriter.cs
   trunk/semweb/Resource.cs
   trunk/semweb/SQLStore.cs
   trunk/semweb/Statement.cs
   trunk/semweb/Store.cs
   trunk/semweb/Util.cs
   trunk/src/MetadataStore.cs
   trunk/src/f-spot.in

Modified: trunk/Makefile.am
==============================================================================
--- trunk/Makefile.am	(original)
+++ trunk/Makefile.am	Fri May 16 18:52:24 2008
@@ -2,7 +2,6 @@
 	$(subdirs)	\
 	$(DIR_DBUS)	\
 	$(DIR_DBUS_GLIB)\
-	$(DIR_SEMWEB)	\
 	docs		\
 	glitz-sharp	\
 	gnome-keyring-sharp \

Modified: trunk/Makefile.include
==============================================================================
--- trunk/Makefile.include	(original)
+++ trunk/Makefile.include	Fri May 16 18:52:24 2008
@@ -15,6 +15,7 @@
 DIR_KEYRING = $(top_builddir)/gnome-keyring-sharp
 DIR_LIBEOG = $(top_builddir)/libeog
 DIR_LIBFSPOT = $(top_builddir)/libfspot
+DIR_SEMWEB = $(top_builddir)/semweb
 DIR_SRC = $(top_builddir)/src
 DIR_TAO_OPENGL = $(top_builddir)/Tao/Tao.OpenGl
 DIR_TAO_GLU = $(top_builddir)/Tao/Tao.OpenGl.Glu
@@ -30,6 +31,7 @@
 	-r:$(DIR_ADDINS_ADDINS)/Mono.Addins.dll		\
 	-r:$(DIR_ADDINS_SETUP)/Mono.Addins.Setup.dll	\
 	-r:$(DIR_ADDINS_GUI)/Mono.Addins.Gui.dll
+LINK_SEMWEB = -r:$(DIR_SEMWEB)/SemWeb.dll
 LINK_TAO = 								\
 	-r:$(DIR_TAO_OPENGL)/Tao.OpenGl.dll				\
 	-r:$(DIR_TAO_GLU)/Tao.OpenGl.Glu.dll				\

Modified: trunk/configure.in
==============================================================================
--- trunk/configure.in	(original)
+++ trunk/configure.in	Fri May 16 18:52:24 2008
@@ -64,7 +64,6 @@
 BEAGLE_REQUIRED=0.3.0
 NDESK_DBUS_REQUIRED=0.4.2
 NDESK_DBUS_GLIB_REQUIRED=0.3.0
-SEMWEB_REQUIRED=1.05
 MONO_CAIRO_REQUIRED=1.2.4
 dnl -- this check is 	
 LCMS_REQUIRED=1.12
@@ -161,7 +160,7 @@
 dnl -- dbus-sharp: link or bundle ?
 PKG_CHECK_MODULES(NDESK_DBUS, ndesk-dbus-1.0 >= $NDESK_DBUS_REQUIRED ndesk-dbus-glib-1.0 >= $NDESK_DBUS_GLIB_REQUIRED,
 [
-	LINK_DBUS="-pkg:ndesk-dbus-1.0 -pkg:ndesk-dbus-glib-1.0 "
+	LINK_DBUS="-pkg:ndesk-dbus-1.0 -pkg:ndesk-dbus-glib-1.0"
 	DIR_DBUS=""
 	DIR_DBUS_GLIB=""
 	PATH_DBUS=""
@@ -177,22 +176,6 @@
 AC_SUBST(LINK_DBUS)
 AC_SUBST(PATH_DBUS)
 
-dnl -- semweb: link or bundle ?
-PKG_CHECK_MODULES(SEMWEB, semweb >= $SEMWEB_REQUIRED,
-[
-	LINK_SEMWEB="-pkg:semweb "
-	DIR_SEMWEB=""
-	PATH_SEMWEB=""
-],
-[
-	LINK_SEMWEB='-r:$(top_builddir)/semweb/SemWeb.dll'
-	DIR_SEMWEB="semweb"
-	PATH_SEMWEB='../semweb:'
-])
-AC_SUBST(DIR_SEMWEB)
-AC_SUBST(LINK_SEMWEB)
-AC_SUBST(PATH_SEMWEB)
-
 dnl -- nunit ... workaround for ubuntu
 NUNIT_DEFINES='-d:ENABLE_NUNIT'
 PKG_CHECK_MODULES(MONO_NUNIT, mono-nunit >= $MONO_REQUIRED,

Modified: trunk/semweb/Algos.cs
==============================================================================
--- trunk/semweb/Algos.cs	(original)
+++ trunk/semweb/Algos.cs	Fri May 16 18:52:24 2008
@@ -14,9 +14,6 @@
 			this.b = b;
 		}
 		public bool Distinct { get { return a.Distinct; } }
-		public bool Contains(Resource resource) {
-			return a.Contains(resource) || b.Contains(resource);
-		}
 		public bool Contains(Statement template) {
 			return Store.DefaultContains(this, template);
 		}
@@ -128,7 +125,7 @@
 				// against the whole store, rather than the MSG in
 				// isolation.  But that gets much too expensive.
 				MemoryStore msgremoved = new MemoryStore();
-				MakeLeanMSG(new Store(msg), msgg.GetBNodes(), msgremoved);
+				MakeLeanMSG(msg, msgg.GetBNodes(), msgremoved);
 				
 				// Whatever was removed from msg, remove it from the main graph.
 				store.RemoveAll(msgremoved.ToArray());
@@ -145,7 +142,7 @@
 				// The GraphMatch will treat all blank nodes in
 				// msg as variables.
 				GraphMatch match = new GraphMatch(msg);
-				QueryResultBuffer sink = new QueryResultBuffer();
+				QueryResultBufferSink sink = new QueryResultBufferSink();
 				match.Run(new SubtractionSource(store, msg), sink);
 				if (sink.Bindings.Count > 0) {
 					// This MSG can be removed.
@@ -511,7 +508,7 @@
 			return ret;
 		}
 		
-		public static void FindMSG(SelectableSource store, Entity node, StatementSink msg) {
+		public static void FindMSG(SelectableSource store, Entity node, Store msg) {
 			if (node.Uri != null) throw new ArgumentException("node must be anonymous");
 			
 			ResSet nodesSeen = new ResSet();
@@ -535,14 +532,14 @@
 		}
 		
 		private class Sink : StatementSink {
-			StatementSink msg;
+			Store msg;
 			ResSet add;
-			public Sink(StatementSink msg, ResSet add) {
+			public Sink(Store msg, ResSet add) {
 				this.msg = msg;
 				this.add = add;
 			}
 			public bool Add(Statement s) {
-				if (msg is SelectableSource && ((SelectableSource)msg).Contains(s)) return true;
+				if (msg.Contains(s)) return true;
 				msg.Add(s);
 				if (s.Subject.Uri == null) add.Add(s.Subject);
 				if (s.Predicate.Uri == null) add.Add(s.Predicate);

Modified: trunk/semweb/AssemblyInfo.cs
==============================================================================
--- trunk/semweb/AssemblyInfo.cs	(original)
+++ trunk/semweb/AssemblyInfo.cs	Fri May 16 18:52:24 2008
@@ -1,18 +1,18 @@
-using System.Reflection;
-using System.Runtime.CompilerServices;
-
-[assembly: AssemblyTitle("SemWeb")]
-[assembly: AssemblyDescription("A library for applications using RDF and the semantic web.")]
-[assembly: AssemblyConfiguration("")]
-[assembly: AssemblyCompany("")]
-[assembly: AssemblyProduct("")]
-[assembly: AssemblyCopyright("Copyright (c) 2008 Joshua Tauberer <http://razor.occams.info>")]
-[assembly: AssemblyTrademark("")]
-[assembly: AssemblyCulture("")]		
-
-[assembly: AssemblyVersion("1.0.5.0")]
-[assembly: AssemblyDelaySign(false)]
-[assembly: AssemblyKeyFile("")]
-[assembly: AssemblyKeyName("")]
-
-
+using System.Reflection;
+using System.Runtime.CompilerServices;
+
+[assembly: AssemblyTitle("SemWeb")]
+[assembly: AssemblyDescription("A library for applications using RDF and the semantic web.")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("")]
+[assembly: AssemblyCopyright("Copyright (c) 2006 Joshua Tauberer <tauberer for net>")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]		
+
+[assembly: AssemblyVersion("0.7.1.0")]
+[assembly: AssemblyDelaySign(false)]
+[assembly: AssemblyKeyFile("")]
+[assembly: AssemblyKeyName("")]
+
+

Modified: trunk/semweb/Inference.cs
==============================================================================
--- trunk/semweb/Inference.cs	(original)
+++ trunk/semweb/Inference.cs	Fri May 16 18:52:24 2008
@@ -1,133 +1,8 @@
 using System;
-
-#if !DOTNET2
 using System.Collections;
-#else
-using System.Collections.Generic;
-#endif
-
 using SemWeb;
-using SemWeb.Query;
 
 namespace SemWeb.Inference {
-
-	public abstract class Reasoner {
-		public abstract bool Distinct { get; } // assume targetModel.Distinct is true, *then* would Select be distinct?
-
-		public void Select(Statement template, SelectableSource targetModel, StatementSink sink) {
-			Select(new SelectFilter(template), targetModel, sink);
-		}
-		
-		public abstract void Select(SelectFilter filter, SelectableSource targetModel, StatementSink sink);
-		
-		public abstract MetaQueryResult MetaQuery(Statement[] graph, QueryOptions options, SelectableSource targetModel);
-		public abstract void Query(Statement[] graph, QueryOptions options, SelectableSource targetModel, QueryResultSink result);
-	}
-	
-	public class SimpleEntailment : Reasoner {
-		public override bool Distinct { get { return true; } }
-		
-		public override void Select(SelectFilter filter, SelectableSource targetModel, StatementSink sink) {
-			targetModel.Select(filter, sink);
-		}
-		
-		public override MetaQueryResult MetaQuery(Statement[] graph, SemWeb.Query.QueryOptions options, SelectableSource source) {
-			SemWeb.Query.MetaQueryResult ret = new SemWeb.Query.MetaQueryResult();
-			
-			ret.QuerySupported = true;
-			
-			ret.NoData = new bool[graph.Length];
-			for (int i = 0; i < graph.Length; i++) {
-				// Take this statement and replace variables by nulls
-				// to make it a statement template.
-				Statement st = graph[i];
-				for (int j = 0; j < 4; j++) {
-					if (st.GetComponent(j) is Variable)
-						st.SetComponent(j, null);
-				}
-				
-				// See if the store contains this template.
-				if (st != Statement.All && !source.Contains(st)) {
-					ret.NoData[i] = true;
-					continue;
-				}
-			
-				// Process it further in case we have variables
-				// with known values, in which case if none of the
-				// known values is in the store, we also know this
-				// statement is unanswerable.
-				for (int j = 0; j < 4; j++) {
-					Resource r = graph[i].GetComponent(j);
-					
-					// No need to check the following given the check above.
-					//if (r != null && !(r is Variable) && !source.Contains(r))
-					//	ret.NoData[i] = true;
-					
-					if (r != null && r is Variable && options.VariableKnownValues != null && 
-					#if !DOTNET2
-					options.VariableKnownValues.Contains((Variable)r)
-					#else
-					options.VariableKnownValues.ContainsKey((Variable)r)
-					#endif
-					) {
-						bool found = false;
-						#if !DOTNET2
-						foreach (Resource s in (ICollection)options.VariableKnownValues[(Variable)r]) {
-						#else
-						foreach (Resource s in (ICollection<Resource>)options.VariableKnownValues[(Variable)r]) {
-						#endif
-							if (source.Contains(s)) {
-								found = true;
-								break;
-							}
-						}
-						if (!found) {
-							ret.NoData[i] = true;
-						}
-					}
-				}
-			}
-			
-			return ret;
-		}
-		
-		public override void Query(Statement[] graph, SemWeb.Query.QueryOptions options, SelectableSource targetModel, QueryResultSink result) {
-			SemWeb.Query.GraphMatch q = new SemWeb.Query.GraphMatch();
-			foreach (Statement s in graph)
-				q.AddGraphStatement(s);
-				
-			q.ReturnLimit = options.Limit;
-			
-			if (options.VariableKnownValues != null) {
-			#if !DOTNET2
-			foreach (DictionaryEntry ent in options.VariableKnownValues)
-				q.SetVariableRange((Variable)ent.Key, (ICollection)ent.Value);
-			#else
-			foreach (KeyValuePair<Variable,ICollection<Resource>> ent in options.VariableKnownValues)
-				q.SetVariableRange(ent.Key, ent.Value);
-			#endif
-			}
-
-			if (options.VariableLiteralFilters != null) {			
-			#if !DOTNET2
-			foreach (DictionaryEntry ent in options.VariableLiteralFilters)
-				foreach (LiteralFilter filter in (ICollection)ent.Value)
-					q.AddLiteralFilter((Variable)ent.Key, filter);
-			#else
-			foreach (KeyValuePair<Variable,ICollection<LiteralFilter>> ent in options.VariableLiteralFilters)
-				foreach (LiteralFilter filter in ent.Value)
-					q.AddLiteralFilter(ent.Key, filter);
-			#endif
-			}
-			
-			if (options.DistinguishedVariables != null) {
-				foreach (Variable v in options.DistinguishedVariables)
-					q.SetDistinguishedVariable(v);
-			}
-
-			q.Run(targetModel, result);
-		}
-	}
 	
 	public class Rule {
 		public readonly Statement[] Antecedent;
@@ -139,29 +14,22 @@
 		}
 		
 		public override string ToString() {
-			string ret = "";
-			if (Antecedent.Length == 0) {
-				ret += "(axiom) ";
-			} else {
-				if (Antecedent.Length > 1) ret += "{";
-				foreach (Statement s in Antecedent)
-					ret += " " + s.ToString();
-				if (Antecedent.Length > 1) ret += " }";
-				ret += " => ";
-			}
-			if (Consequent.Length > 1) ret += "{";
+			string ret = "{";
+			foreach (Statement s in Antecedent)
+				ret += " " + s.ToString();
+			ret += " } => {";
 			foreach (Statement s in Consequent)
 				ret += " " + s.ToString();
-			if (Consequent.Length > 1) ret += " }";
+			ret += " }";
 			return ret;
 		}
 	}
 	
 	public class ProofStep {
 		public readonly Rule Rule;
-		public readonly System.Collections.IDictionary Substitutions;
+		public readonly IDictionary Substitutions;
 		
-		public ProofStep(Rule rule, System.Collections.IDictionary substitutions) {
+		public ProofStep(Rule rule, IDictionary substitutions) {
 			Rule = rule;
 			Substitutions = substitutions;
 		}
@@ -175,32 +43,5 @@
 			Proved = proved;
 			Steps = steps;
 		}
-		
-		public override string ToString () {
-			System.Text.StringBuilder ret = new System.Text.StringBuilder();
-
-			ret.Append("Proved: ");
-			foreach (Statement s in Proved)
-				ret.Append(s.ToString());
-			ret.Append("\n");
-
-			foreach (ProofStep step in Steps) {
-				ret.Append("\t");
-				ret.Append(step.Rule.ToString());
-				ret.Append("\n");
-				System.Collections.ArrayList vars = new System.Collections.ArrayList(step.Substitutions.Keys);
-				vars.Sort();
-				foreach (Variable v in vars) {
-					ret.Append("\t\t");
-					ret.Append(v);
-					ret.Append(" => ");
-					ret.Append(step.Substitutions[v]);
-					ret.Append("\n");
-				}
-			}
-			
-			return ret.ToString();
-		}
-
 	}
 }

Modified: trunk/semweb/LiteralFilters.cs
==============================================================================
--- trunk/semweb/LiteralFilters.cs	(original)
+++ trunk/semweb/LiteralFilters.cs	Fri May 16 18:52:24 2008
@@ -110,17 +110,6 @@
 		}
 	}
 
-	public class StringEndsWithFilter : LiteralFilter {
-		public readonly string Pattern;
-		public StringEndsWithFilter(string pattern) {
-			Pattern = pattern;
-		}
-		public override bool Filter(Literal resource, SelectableSource targetModel) {
-			string v = resource.Value;
-			return v.EndsWith(Pattern);
-		}
-	}
-
 	public class NumericCompareFilter : LiteralFilter {
 		public readonly Decimal Number;
 		public readonly CompType Type;
@@ -136,7 +125,7 @@
 				Decimal i = Decimal.Parse(v);
 				int c = i.CompareTo(Number);
 				return CompareFilter(c, Type);
-			} catch (Exception) {
+			} catch (Exception e) {
 				return false;
 			}
 		}
@@ -157,7 +146,7 @@
 				DateTime i = DateTime.Parse(v);
 				int c = i.CompareTo(Value);
 				return CompareFilter(c, Type);
-			} catch (Exception) {
+			} catch (Exception e) {
 				return false;
 			}
 		}
@@ -178,7 +167,7 @@
 				TimeSpan i = TimeSpan.Parse(v);
 				int c = i.CompareTo(Value);
 				return CompareFilter(c, Type);
-			} catch (Exception) {
+			} catch (Exception e) {
 				return false;
 			}
 		}

Modified: trunk/semweb/Makefile.am
==============================================================================
--- trunk/semweb/Makefile.am	(original)
+++ trunk/semweb/Makefile.am	Fri May 16 18:52:24 2008
@@ -3,11 +3,6 @@
 ASSEMBLY_NAME = SemWeb
 
 ASSEMBLY_SOURCES =				\
-		$(srcdir)/SparqlClient.cs	\
-		$(srcdir)/Euler.cs		\
-		$(srcdir)/Interfaces.cs		\
-		$(srcdir)/GraphMatch.cs		\
-		$(srcdir)/SpecialRelations.cs	\
                 $(srcdir)/AssemblyInfo.cs       \
                 $(srcdir)/NamespaceManager.cs   \
                 $(srcdir)/Util.cs               \
@@ -23,11 +18,14 @@
                 $(srcdir)/RdfWriter.cs          \
                 $(srcdir)/RdfXmlWriter.cs       \
                 $(srcdir)/N3Writer.cs           \
+                $(srcdir)/RSquary.cs            \
                 $(srcdir)/LiteralFilters.cs     \
                 $(srcdir)/Query.cs              \
                 $(srcdir)/Inference.cs          \
                 $(srcdir)/RDFS.cs               \
-                $(srcdir)/Algos.cs 
+                $(srcdir)/Algos.cs              \
+                $(srcdir)/Remote.cs             \
+                $(srcdir)/XPathSemWebNavigator.cs             
 
 REFS =			\
 	-r:System.Data	\

Modified: trunk/semweb/MemoryStore.cs
==============================================================================
--- trunk/semweb/MemoryStore.cs	(original)
+++ trunk/semweb/MemoryStore.cs	Fri May 16 18:52:24 2008
@@ -6,81 +6,8 @@
 using SemWeb.Util;
 
 namespace SemWeb {
-	public class MemoryStore : Store, 
-#if DOTNET2
-	System.Collections.Generic.IEnumerable<Statement>
-#else
-	IEnumerable
-#endif
-	{
-	
-		internal StoreImpl impl;
-
-		public MemoryStore()
-			: this(new StoreImpl()) {
-		}
-
-		public MemoryStore(StatementSource source)
-			: this() {
-			Import(source);
-		}
-		
-		public MemoryStore(Statement[] statements)
-			: this(new StoreImpl(statements)) {
-		}
-		
-		private MemoryStore(StoreImpl impl) {
-			this.impl = impl;
-			AddSource2(impl);
-		}
-		
-		public override void AddSource(SelectableSource store) {
-			throw new InvalidOperationException("AddSource is not valid on the MemoryStore.");
-		}
-		
-		public override void AddSource(SelectableSource store, string uri) {
-			throw new InvalidOperationException("AddSource is not valid on the MemoryStore.");
-		}
-
-		public Statement this[int index] {
-			get {
-				return impl[index];
-			}
-		}
-
-		public Statement[] ToArray() {
-			return impl.ToArray();
-		}
-
-		#if DOTNET2
-		System.Collections.Generic.IEnumerator<Statement> System.Collections.Generic.IEnumerable<Statement>.GetEnumerator() {
-			return ((System.Collections.Generic.IEnumerable<Statement>)impl).GetEnumerator();
-		}
-		#endif
-		IEnumerator IEnumerable.GetEnumerator() {
-			return ((IEnumerable)impl).GetEnumerator();
-		}
-
-		internal bool allowIndexing { set { impl.allowIndexing = value; } }
-		internal bool checkForDuplicates { set { impl.checkForDuplicates = value; } }
-
-
-	internal class StoreImpl : SelectableSource, StaticSource, ModifiableSource, 
-#if DOTNET2
-	System.Collections.Generic.IEnumerable<Statement>
-#else
-	IEnumerable
-#endif
-	
-	{
-		#if DOTNET2
-		private class StatementList : System.Collections.Generic.List<Statement> {
-			public StatementList() : base() { }
-			public StatementList(Statement[] statements) : base(statements) { }
-		}
-		#endif
-
-		StatementList statements;
+	public class MemoryStore : Store, SupportsPersistableBNodes, IEnumerable {
+		StatementList statements;
 		
 		Hashtable statementsAboutSubject = new Hashtable();
 		Hashtable statementsAboutObject = new Hashtable();
@@ -92,33 +19,29 @@
 		
 		string guid = null;
 		Hashtable pbnodeToId = null;
-		Hashtable pbnodeFromId = null;
-		
-		const string rdfli = NS.RDF + "_";
-		
-		public StoreImpl() {
+		Hashtable pbnodeFromId = null;
+		
+		public MemoryStore() {
 			statements = new StatementList();
 		}
 		
-		public StoreImpl(StatementSource source) : this() {
+		public MemoryStore(StatementSource source) : this() {
 			Import(source);
 		}
 		
-		public StoreImpl(Statement[] statements) {
+		public MemoryStore(Statement[] statements) {
 			this.statements = new StatementList(statements);
 		}
 
-		public Statement[] ToArray() {
-#if DOTNET2
-			return statements.ToArray();
-#else
+		public Statement[] ToArray() {
 			return (Statement[])statements.ToArray(typeof(Statement));
-#endif
-		}
+		}
 
-		public bool Distinct { get { return distinct; } }
+		public IList Statements { get { return statements.ToArray(); } }
 		
-		public int StatementCount { get { return statements.Count; } }
+		public override bool Distinct { get { return distinct; } }
+		
+		public override int StatementCount { get { return statements.Count; } }
 		
 		public Statement this[int index] {
 			get {
@@ -126,16 +49,11 @@
 			}
 		}
 		
-#if DOTNET2
-		System.Collections.Generic.IEnumerator<Statement> System.Collections.Generic.IEnumerable<Statement>.GetEnumerator() {
-			return statements.GetEnumerator();
-		}
-#endif
 		IEnumerator IEnumerable.GetEnumerator() {
 			return statements.GetEnumerator();
 		}
 		
-		public void Clear() {
+		public override void Clear() {
 			statements.Clear();
 			statementsAboutSubject.Clear();
 			statementsAboutObject.Clear();
@@ -149,14 +67,9 @@
 				from[entity] = ret;
 			}
 			return ret;
-		}
-		
-		bool StatementSink.Add(Statement statement) {
-			Add(statement);
-			return true;
 		}
 		
-		public void Add(Statement statement) {
+		public override void Add(Statement statement) {
 			if (statement.AnyNull) throw new ArgumentNullException();
 			if (checkForDuplicates && Contains(statement)) return;
 			statements.Add(statement);
@@ -167,13 +80,13 @@
 			if (!checkForDuplicates) distinct = false;
 		}
 		
-		public void Import(StatementSource source) {
+		public override void Import(StatementSource source) {
 			bool newDistinct = checkForDuplicates || ((StatementCount==0) && source.Distinct);
-			source.Select(this);
+			base.Import(source); // distinct set to false if !checkForDuplicates
 			distinct = newDistinct;
 		}
 		
-		public void Remove(Statement statement) {
+		public override void Remove(Statement statement) {
 			if (statement.AnyNull) {
 				for (int i = 0; i < statements.Count; i++) {
 					Statement s = (Statement)statements[i];
@@ -192,16 +105,11 @@
 					GetIndexArray(statementsAboutObject, statement.Object).Remove(statement);
 				}
 			}
-		}
-		
-		public void RemoveAll(Statement[] statements) {
-			foreach (Statement t in statements)
-				Remove(t);
 		}
 		
-		public Entity[] GetEntities() {
+		public override Entity[] GetEntities() {
 			Hashtable h = new Hashtable();
-			foreach (Statement s in statements) {
+			foreach (Statement s in Statements) {
 				if (s.Subject != null) h[s.Subject] = h;
 				if (s.Predicate != null) h[s.Predicate] = h;
 				if (s.Object != null && s.Object is Entity) h[s.Object] = h;
@@ -210,16 +118,16 @@
 			return (Entity[])new ArrayList(h.Keys).ToArray(typeof(Entity));
 		}
 		
-		public Entity[] GetPredicates() {
+		public override Entity[] GetPredicates() {
 			Hashtable h = new Hashtable();
-			foreach (Statement s in statements)
+			foreach (Statement s in Statements)
 				h[s.Predicate] = h;
 			return (Entity[])new ArrayList(h.Keys).ToArray(typeof(Entity));
 		}
 
-		public Entity[] GetMetas() {
+		public override Entity[] GetMetas() {
 			Hashtable h = new Hashtable();
-			foreach (Statement s in statements)
+			foreach (Statement s in Statements)
 				h[s.Meta] = h;
 			return (Entity[])new ArrayList(h.Keys).ToArray(typeof(Entity));
 		}
@@ -227,13 +135,9 @@
 		private void ShorterList(ref StatementList list1, StatementList list2) {
 			if (list2.Count < list1.Count)
 				list1 = list2;
-		}
-		
-		public void Select(StatementSink result) {
-			Select(Statement.All, result);
 		}
 		
-		public void Select(Statement template, StatementSink result) {
+		public override void Select(Statement template, StatementSink result) {
 			StatementList source = statements;
 			
 			// The first time select is called, turn indexing on for the store.
@@ -251,26 +155,17 @@
 			if (template.Subject != null) ShorterList(ref source, GetIndexArray(statementsAboutSubject, template.Subject));
 			else if (template.Object != null) ShorterList(ref source, GetIndexArray(statementsAboutObject, template.Object));
 			
-			if (source == null) return;
-			
-			bool isRdfsMemberPredicate = (template.Predicate != null && template.Predicate.Uri != null
-				&& template.Predicate.Uri == NS.RDFS + "member");
-			if (isRdfsMemberPredicate)
-				template.Predicate = null;
+			if (source == null) return;
 			
 			for (int i = 0; i < source.Count; i++) {
 				Statement statement = source[i];
 				if (!template.Matches(statement))
-					continue;
-				
-				if (isRdfsMemberPredicate && (statement.Predicate.Uri == null || !statement.Predicate.Uri.StartsWith(rdfli)))
-					continue;
-					
+					continue;
 				if (!result.Add(statement)) return;
 			}
 		}
 
-		public void Select(SelectFilter filter, StatementSink result) {
+		public override void Select(SelectFilter filter, StatementSink result) {
 			ResSet
 				s = filter.Subjects == null ? null : new ResSet(filter.Subjects),
 				p = filter.Predicates == null ? null : new ResSet(filter.Predicates),
@@ -285,23 +180,9 @@
 				if (filter.LiteralFilters != null && !LiteralFilter.MatchesFilters(st.Object, filter.LiteralFilters, this)) continue;
 				if (!result.Add(st)) return;
 			}
-		}
-		
-		public bool Contains(Resource r) {
-			foreach (Statement s in statements) {
-				if (s.Subject == r) return true;
-				if (s.Predicate == r) return true;
-				if (s.Object == r) return true;
-				if (s.Meta == r) return true;
-			}
-			return false;
-		}
-		
-		public bool Contains(Statement template) {
-			return Store.DefaultContains(this, template);
 		}
 
-		public void Replace(Entity a, Entity b) {
+		public override void Replace(Entity a, Entity b) {
 			MemoryStore removals = new MemoryStore();
 			MemoryStore additions = new MemoryStore();
 			foreach (Statement statement in statements) {
@@ -314,34 +195,38 @@
 			Import(additions);
 		}
 		
-		public void Replace(Statement find, Statement replacement) {
-			Remove(find);
-			Add(replacement);
+		public override void Replace(Statement find, Statement replacement) {
+			if (find.AnyNull) throw new ArgumentNullException("find");
+			if (replacement.AnyNull) throw new ArgumentNullException("replacement");
+			if (find == replacement) return;
+			
+			foreach (Statement match in Select(find)) {
+				Remove(match);
+				Add(replacement);
+				break; // should match just one statement anyway
+			}
 		}
 
-		private string GetStoreGuid() {
+		string SupportsPersistableBNodes.GetStoreGuid() {
 			if (guid == null) guid = Guid.NewGuid().ToString("N");;
 			return guid;
 		}
 		
-		public string GetPersistentBNodeId(BNode node) {
+		string SupportsPersistableBNodes.GetNodeId(BNode node) {
 			if (pbnodeToId == null) {
 				pbnodeToId = new Hashtable();
 				pbnodeFromId = new Hashtable();
 			}
 			if (pbnodeToId.ContainsKey(node)) return (string)pbnodeToId[node];
-			string id = GetStoreGuid() + ":" + pbnodeToId.Count.ToString();
+			string id = pbnodeToId.Count.ToString();
 			pbnodeToId[node] = id;
 			pbnodeFromId[id] = node;
 			return id;
 		}
 		
-		public BNode GetBNodeFromPersistentId(string persistentId) {
+		BNode SupportsPersistableBNodes.GetNodeFromId(string persistentId) {
 			if (pbnodeFromId == null) return null;
 			return (BNode)pbnodeFromId[persistentId];
 		}
 	}
-
-	}
-
 }

Modified: trunk/semweb/N3Reader.cs
==============================================================================
--- trunk/semweb/N3Reader.cs	(original)
+++ trunk/semweb/N3Reader.cs	Fri May 16 18:52:24 2008
@@ -11,7 +11,6 @@
 	public class N3Reader : RdfReader {
 		Resource PrefixResource = new Literal("@prefix");
 		Resource KeywordsResource = new Literal("@keywords");
-		Resource BaseResource = new Literal("@base");
 		
 		TextReader sourcestream;
 		NamespaceManager namespaces = new NamespaceManager();
@@ -23,7 +22,8 @@
 		//Entity entOWLSAMEAS = "http://www.w3.org/2002/07/owl#sameAs";;
 		Entity entDAMLEQUIV = "http://www.daml.org/2000/12/daml+oil#equivalentTo";;
 		Entity entLOGIMPLIES = "http://www.w3.org/2000/10/swap/log#implies";;
-		Entity entGRAPHCONTAINS = "http://razor.occams.info/code/semweb/internaluris/graphContains";;
+		
+		bool addFailuresAsWarnings = false;
 		
 		public N3Reader(TextReader source) {
 			this.sourcestream = source;
@@ -44,7 +44,6 @@
 			public Entity meta;
 			public bool UsingKeywords;
 			public Hashtable Keywords;
-			public Entity overrideMeta;
 			
 			public Location Location { get { return new Location(source.Line, source.Col); } }
 		}
@@ -65,8 +64,8 @@
 		private bool ReadStatement(ParseContext context) {
 			Location loc = context.Location;
 			
-			bool reverse, forgetBNode;
-			Resource subject = ReadResource(context, true, out reverse, out forgetBNode);
+			bool reverse;
+			Resource subject = ReadResource(context, out reverse);
 			if (subject == null) return false;
 			if (reverse) OnError("is...of not allowed on a subject", loc);
 			
@@ -76,8 +75,7 @@
 				if (qname == null || !qname.EndsWith(":")) OnError("When using @prefix, the prefix identifier must end with a colon", loc);
 				
 				loc = context.Location;
-				bool fb2;
-				Resource uri = ReadResource(context, false, out reverse, out fb2);
+				Resource uri = ReadResource(context, out reverse);
 				if (uri == null) OnError("Expecting a URI", loc);
 				if (reverse) OnError("is...of not allowed here", loc);
 				namespaces.AddNamespace(uri.Uri, qname.Substring(0, qname.Length-1));
@@ -109,32 +107,15 @@
 				return true;
 			}
 			
-			if ((object)subject == (object)BaseResource) {
-				loc = context.Location;
-				bool fb2;
-				Resource uri = ReadResource(context, false, out reverse, out fb2);
-				if (uri == null || uri.Uri == null) OnError("Expecting a URI", loc);
-				if (reverse) OnError("is...of not allowed here", loc);
-				BaseUri = uri.Uri;
-				
-				loc = context.Location;
-				char punc = ReadPunc(context.source);
-				if (punc != '.')
-					OnError("Expected a period but found '" + punc + "'", loc);
-				return true;
-			}
-
 			// It's possible to just assert the presence of an entity
 			// by following the entity with a period, or a } to end
 			// a reified context.
 			if (NextPunc(context.source) == '.') {
 				context.source.Read();
-				if (forgetBNode) DoForget(subject, context);
 				return true;
 			}
 			if (NextPunc(context.source) == '}') {
 				context.source.Read();
-				if (forgetBNode) DoForget(subject, context);
 				return false; // end of block
 			}
 			
@@ -144,7 +125,6 @@
 			if (period != '.' && period != '}')
 				OnError("Expected a period but found '" + period + "'", loc);
 			if (period == '}') return false;
-			if (forgetBNode) DoForget(subject, context);
 			return true;
 		}
 		
@@ -166,18 +146,12 @@
 		}
 		
 		private char ReadPredicate(Resource subject, ParseContext context) {
-			bool reverse, forgetBNode;
+			bool reverse;
 			Location loc = context.Location;
-			Resource predicate = ReadResource(context, false, out reverse, out forgetBNode);
+			Resource predicate = ReadResource(context, out reverse);
 			if (predicate == null) OnError("Expecting a predicate", loc);
 			if (predicate is Literal) OnError("Predicates cannot be literals", loc);
 			
-			if (predicate == entGRAPHCONTAINS) {
-				context.overrideMeta = subject as Entity;
-			} else {
-				context.overrideMeta = null;
-			}
-			
 			char punctuation = ',';
 			while (punctuation == ',') {
 				ReadObject(subject, (Entity)predicate, context, reverse);
@@ -187,30 +161,24 @@
 			if (punctuation != '.' && punctuation != ';' && punctuation != ']' && punctuation != '}')
 				OnError("Expecting a period, semicolon, comma, close-bracket, or close-brace but found '" + punctuation + "'", loc);
 			
-			if (forgetBNode) DoForget(predicate, context);
-			
 			return punctuation;
 		}
 		
 		private void ReadObject(Resource subject, Entity predicate, ParseContext context, bool reverse) {
-			bool reverse2, forgetBNode;
+			bool reverse2;
 			Location loc = context.Location;
-			Resource value = ReadResource(context, false, out reverse2, out forgetBNode);
+			Resource value = ReadResource(context, out reverse2);
 			if (value == null) OnError("Expecting a resource or literal object", loc);
 			if (reverse2) OnError("is...of not allowed on objects", loc);
 			
 			loc = context.Location;
-			if (predicate == entGRAPHCONTAINS) {
-				// don't add the statement, it was enough to associate the meta node
-			} else if (!reverse) {
+			if (!reverse) {
 				if (subject is Literal) OnError("Subjects of statements cannot be literals", loc);			
 				Add(context.store, new Statement((Entity)subject, predicate, value, context.meta), loc);
 			} else {
 				if (value is Literal) OnError("A literal cannot be the object of a reverse-predicate statement", loc);
 				Add(context.store, new Statement((Entity)value, predicate, subject, context.meta), loc);
 			}
-
-			if (forgetBNode) DoForget(value, context);
 		}
 		
 		private void ReadWhitespace(MyReader source) {
@@ -388,7 +356,7 @@
 				// A variable: \?[a-zA-Z_][a-zA-Z0-9_]*
 				while (true) {
 					int c = source.Peek();
-					if (c == -1 || (!Entity.ValidateUriIsIUnreserved((char)c) && c != ':') || c == '.') break;
+					if (c == -1 || (!char.IsLetterOrDigit((char)c) && c != '-' && c != '_' && c != ':')) break;					
 					b.Append((char)source.Read());
 				}
 			
@@ -412,11 +380,6 @@
 			} else if (firstchar == '=') {
 				if (source.Peek() == (int)'>')
 					b.Append((char)source.Read());
-				
-				if (source.Peek() == (int)':' && source.Peek2() == (int)'>') { // SPECIAL EXTENSION "=:>"
-					b.Append((char)source.Read());
-					b.Append((char)source.Read());
-				}
 			
 			} else if (firstchar == '[') {
 				// The start of an anonymous node.
@@ -442,18 +405,18 @@
 			return b.ToString();
 		}
 		
-		private Resource ReadResource(ParseContext context, bool allowDirective, out bool reverse, out bool forgetBNode) {
+		private Resource ReadResource(ParseContext context, out bool reverse) {
 			Location loc = context.Location;
 			
-			Resource res = ReadResource2(context, allowDirective, out reverse, out forgetBNode);
+			Resource res = ReadResource2(context, out reverse);
 			
 			ReadWhitespace(context.source);
 			while (context.source.Peek() == '!' || context.source.Peek() == '^' || (context.source.Peek() == '.' && context.source.Peek2() != -1 && char.IsLetter((char)context.source.Peek2())) ) {
 				int pathType = context.source.Read();
 				
-				bool reverse2, forgetBNode2;
+				bool reverse2;
 				loc = context.Location;
-				Resource path = ReadResource2(context, false, out reverse2, out forgetBNode2);
+				Resource path = ReadResource2(context, out reverse2);
 				if (reverse || reverse2) OnError("is...of is not allowed in path expressions", loc);
 				if (!(path is Entity)) OnError("A path expression cannot be a literal", loc);
 				
@@ -469,11 +432,7 @@
 				
 				Add(context.store, s, loc);
 				
-				if (forgetBNode) DoForget(res, context);
-				if (forgetBNode2) DoForget(path, context);
-				
 				res = anon;
-				forgetBNode = true;
 
 				ReadWhitespace(context.source);
 			}
@@ -514,9 +473,8 @@
 			}
 		}
 			
-		private Resource ReadResource2(ParseContext context, bool allowDirective, out bool reverse, out bool forgetBNode) {
+		private Resource ReadResource2(ParseContext context, out bool reverse) {
 			reverse = false;
-			forgetBNode = false;
 			
 			Location loc = context.Location;
 			
@@ -527,32 +485,15 @@
 			string str = (string)tok;
 			if (str == "")
 				return null;
-				
-			// Directives
 			
-			if (str == "@prefix") {
-				if (allowDirective)
-					return PrefixResource;
-				else
-					OnError("The directive '" + str + "' is not allowed here", loc);
-			}
+			// @ Keywords
 
-			if (str == "@keywords") {
-				if (allowDirective)
-					return KeywordsResource;
-				else
-					OnError("The directive '" + str + "' is not allowed here", loc);
-			}
+			if (str == "@prefix")
+				return PrefixResource;
 
-			if (str == "@base") {
-				if (allowDirective)
-					return BaseResource;
-				else
-					OnError("The directive '" + str + "' is not allowed here", loc);
-			}
+			if (str == "@keywords")
+				return KeywordsResource;
 			
-			// @ Keywords
-
 			if (context.UsingKeywords && context.Keywords.Contains(str))
 				str = "@" + str;
 			if (!context.UsingKeywords &&
@@ -572,17 +513,15 @@
 			if (str == "<=") {
 				reverse = true;
 				return entLOGIMPLIES;
-			}
-			if (str == "=:>") // SPECIAL EXTENSION!
-				return entGRAPHCONTAINS;
+			}				
 
 			if (str == "@has") // ignore this token
-				return ReadResource2(context, false, out reverse, out forgetBNode);
+				return ReadResource2(context, out reverse);
 			
 			if (str == "@is") {
 				// Reverse predicate
 				bool reversetemp;
-				Resource pred = ReadResource2(context, false, out reversetemp, out forgetBNode);
+				Resource pred = ReadResource2(context, out reversetemp);
 				reverse = true;
 				
 				string of = ReadToken(context.source, context) as string;
@@ -602,9 +541,6 @@
 			
 			if (str.StartsWith("<") && str.EndsWith(">")) {
 				string uri = GetAbsoluteUri(BaseUri, str.Substring(1, str.Length-2));
-				string urierror = Entity.ValidateUri(uri);
-				if (urierror != null)
-					OnWarning(urierror, loc);
 				return GetResource(context, uri);
 			}
 			
@@ -640,7 +576,6 @@
 				} else {
 					context.source.Read();
 				}
-				forgetBNode = true;
 				return ret;
 			}
 			
@@ -648,16 +583,15 @@
 			
 			if (str == "(") {
 				// A list
-				Entity head = null, ent = null;
+				Entity ent = null;
 				while (true) {
-					bool rev2, fb2;
-					Resource res = ReadResource(context, false, out rev2, out fb2);
+					bool rev2;
+					Resource res = ReadResource(context, out rev2);
 					if (res == null)
 						break;
 					
 					if (ent == null) {
 						ent = new BNode();
-						head = ent;
 					} else {
 						Entity sub = new BNode();
 						Add(context.store, new Statement(ent, entRDFREST, sub, context.meta), loc);
@@ -665,14 +599,12 @@
 					}
 					
 					Add(context.store, new Statement(ent, entRDFFIRST, res, context.meta), loc);
-					if (fb2) DoForget(res, context);
 				}
-				if (head == null) // No list items.
-					head = entRDFNIL; // according to Turtle spec
+				if (ent == null) // No list items.
+					ent = entRDFNIL; // according to Turtle spec
 				else
 					Add(context.store, new Statement(ent, entRDFREST, entRDFNIL, context.meta), loc);
-				
-				return head;
+				return ent;
 			}
 			
 			if (str == ")")
@@ -684,12 +616,8 @@
 				// ParseContext is a struct, so this gives us a clone.
 				ParseContext newcontext = context;
 				
-				// The formula is denoted by a blank node, unless we set
-				// the override meta flag above.
-				if (context.overrideMeta == null)
-					newcontext.meta = new BNode();
-				else
-					newcontext.meta = context.overrideMeta;
+				// The formula is denoted by a blank node
+				newcontext.meta = new BNode();
 				
 				// According to the spec, _:xxx anonymous nodes are
 				// local to the formula.  But ?$variables (which aren't
@@ -707,18 +635,8 @@
 			
 			// In Turtle, numbers are restricted to [0-9]+, and are datatyped xsd:integer.
 			double numval;
-			if (double.TryParse(str, System.Globalization.NumberStyles.Any, null, out numval)) {
-				if (numval >= long.MinValue && numval <= long.MaxValue && numval == (double)(long)numval)
-					return new Literal(((long)numval).ToString(), null, NS.XMLSCHEMA + "integer");
-				else
-					return new Literal(numval.ToString(), null, NS.XMLSCHEMA + "double");
-			}
-			
-			//BOOLEAN LITERAL
-			
-			if (str == "true" || str == "false") {
-			  return new Literal(str,null,NS.XMLSCHEMA+"boolean");
-			}
+			if (double.TryParse(str, System.Globalization.NumberStyles.Any, null, out numval))
+				return new Literal(numval.ToString());
 			
 			// If @keywords is used, alphanumerics that aren't keywords
 			// are local names in the default namespace.
@@ -735,27 +653,27 @@
 		}
 		
 		private void Add(StatementSink store, Statement statement, Location position) {
-			store.Add(statement);
+			try {
+				store.Add(statement);
+			} catch (Exception e) {
+				if (!addFailuresAsWarnings)
+					OnError("Add failed on statement { " + statement + " }: " + e.Message, position, e);
+				else
+					OnWarning("Add failed on statement { " + statement + " }: " + e.Message, position, e);
+			}
 		}
 		
 		private void OnError(string message, Location position) {
 			throw new ParserException(message + ", line " + position.Line + " col " + position.Col);
 		}
-		private void OnWarning(string message, Location position) {
-			base.OnWarning(message + ", line " + position.Line + " col " + position.Col);
-		}
-		/*private void OnError(string message, Location position, Exception cause) {
+		private void OnError(string message, Location position, Exception cause) {
 			throw new ParserException(message + ", line " + position.Line + " col " + position.Col, cause);
 		}
 		private void OnWarning(string message, Location position, Exception cause) {
 			OnWarning(message + ", line " + position.Line + " col " + position.Col);
-		}*/
-		
-		void DoForget(Resource ent, ParseContext context) {
-			CanForgetBNodes x = context.store as CanForgetBNodes;
-			if (x == null) return;
-			x.ForgetBNode((BNode)ent);
 		}
+		
+	
 	}
 
 	internal class MyReader {
@@ -803,7 +721,6 @@
 			
 			return c;
 		}
-		
 	}
 
 	internal struct Location {

Modified: trunk/semweb/N3Writer.cs
==============================================================================
--- trunk/semweb/N3Writer.cs	(original)
+++ trunk/semweb/N3Writer.cs	Fri May 16 18:52:24 2008
@@ -6,12 +6,11 @@
 using SemWeb;
 
 namespace SemWeb {
-	public class N3Writer : RdfWriter, CanForgetBNodes {
+	public class N3Writer : RdfWriter {
 		TextWriter writer;
-		NamespaceManager2 ns = new NamespaceManager2();
+		NamespaceManager ns = new NamespaceManager();
 		bool hasWritten = false;
 		bool closed = false;
-		bool closeStream = false;
 		
 		string lastSubject = null, lastPredicate = null;
 		
@@ -20,16 +19,13 @@
 		
 		Formats format = Formats.Turtle;
 		
-		private const string xsdInteger = NS.XMLSCHEMA + "integer";
-		private const string xsdDouble = NS.XMLSCHEMA + "double";
-		
 		public enum Formats {
 			NTriples,
 			Turtle,
 			Notation3
 		}
 		
-		public N3Writer(string file) : this(GetWriter(file)) { closeStream = true; }
+		public N3Writer(string file) : this(GetWriter(file)) { }
 
 		public N3Writer(TextWriter writer) {
 			this.writer = writer;
@@ -42,7 +38,7 @@
 		public override void Add(Statement statement) {
 			if (statement.AnyNull) throw new ArgumentNullException();
 			WriteStatement2(URI(statement.Subject), URI(statement.Predicate),
-				statement.Object is Literal ? Literal((Literal)statement.Object) : URI((Entity)statement.Object));
+				statement.Object is Literal ? ((Literal)statement.Object).ToString() : URI((Entity)statement.Object));
 		}
 
 		public override void Close() {
@@ -52,18 +48,9 @@
 				writer.WriteLine(".");
 			closed = true;
 			hasWritten = false;
-			if (closeStream)
-				writer.Close();
-			else
-				writer.Flush();
+			writer.Flush();
 		}
 
-		private string Literal(Literal literal) {
-			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();
-			return literal.ToString();
-		}
 		
 		private string URI(Entity entity) {
 			if (entity is Variable && ((Variable)entity).LocalName != null)
@@ -95,7 +82,7 @@
 				if (ok)
 					return ":" + uri.Substring(len);
 			}
-			if (Format == Formats.NTriples) return "<" + Escape(uri) + ">";
+			if (Format == Formats.NTriples || ns == null) return "<" + Escape(uri) + ">";
 			
 			string ret = ns.Normalize(uri);
 			if (ret[0] != '<') return ret;
@@ -164,21 +151,14 @@
 			closed = false;
 			
 			// Write the prefix directives at the beginning.
-			if (ns.addedPrefixes.Count > 0 && !(Format == Formats.NTriples)) {
-				if (hasWritten) {
-					writer.Write(".\n");
-					lastSubject = null;
-					lastPredicate = null;
-					hasWritten = false; // really means whether a statement is "open", missing a period
-				}
-				foreach (string prefix in ns.addedPrefixes) {
+			if (!hasWritten && ns != null && !(Format == Formats.NTriples)) {
+				foreach (string prefix in ns.GetPrefixes()) {
 					writer.Write("@prefix ");
 					writer.Write(prefix);
 					writer.Write(": <");
 					writer.Write(ns.GetNamespace(prefix));
 					writer.Write("> .\n");
 				}
-				ns.addedPrefixes.Clear();
 			}
 
 			// Repeated subject.
@@ -207,7 +187,7 @@
 			
 			// Start a new statement.
 			} else {
-				if (hasWritten) // finish the last statement
+				if (hasWritten)
 					writer.Write(".\n");
 					
 				WriteThing(subj);
@@ -225,18 +205,5 @@
 			writer.Write(text);
 			writer.Write(" ");
 		}
-	
-		private class NamespaceManager2 : NamespaceManager {
-			public ArrayList addedPrefixes = new ArrayList();
-			public override void AddNamespace(string uri, string prefix) {
-				base.AddNamespace(uri, prefix);
-				addedPrefixes.Add(prefix);
-			}
-		}
-		
-		void CanForgetBNodes.ForgetBNode(BNode bnode) {
-			anonNames.Remove(bnode);
-			anonNameMap.Remove(bnode);
-		}
 	}
 }

Modified: trunk/semweb/NamespaceManager.cs
==============================================================================
--- trunk/semweb/NamespaceManager.cs	(original)
+++ trunk/semweb/NamespaceManager.cs	Fri May 16 18:52:24 2008
@@ -7,8 +7,6 @@
 		public const string RDF = "http://www.w3.org/1999/02/22-rdf-syntax-ns#";;
 		public const string RDFS = "http://www.w3.org/2000/01/rdf-schema#";;
 	
-		public const string XMLSCHEMA = "http://www.w3.org/2001/XMLSchema#";;
-	
 		/*Entity entRDFTYPE = "http://www.w3.org/1999/02/22-rdf-syntax-ns#type";;
 		Entity entRDFFIRST = "http://www.w3.org/1999/02/22-rdf-syntax-ns#first";;
 		Entity entRDFREST = "http://www.w3.org/1999/02/22-rdf-syntax-ns#rest";;
@@ -29,16 +27,11 @@
 			this.parent = parent;
 		}
 		
-		public virtual void AddNamespace(string uri, string prefix) {
+		public void AddNamespace(string uri, string prefix) {
 			atob[uri] = prefix;
 			btoa[prefix] = uri;
 		}
 		
-		public void AddFrom(NamespaceManager nsmgr) {
-			foreach (string uri in nsmgr.GetNamespaces())
-				AddNamespace(uri, nsmgr.GetPrefix(uri));
-		}
-
 		public virtual string GetNamespace(string prefix) {
 			string ret = (string)btoa[prefix];
 			if (ret != null) return ret;
@@ -83,7 +76,7 @@
 			if (Normalize(uri, out prefix, out localname)) {
 				bool ok = true;
 				if (localname.Length == 0) ok = false;
-				else if (!char.IsLetter(localname[0]) && localname[0] != '_') ok = false;
+				else if (!char.IsLetter(localname[0])) ok = false;
 				foreach (char c in localname)
 					if (!char.IsLetterOrDigit(c) && c != '-' && c != '_')
 						ok = false;

Modified: trunk/semweb/Query.cs
==============================================================================
--- trunk/semweb/Query.cs	(original)
+++ trunk/semweb/Query.cs	Fri May 16 18:52:24 2008
@@ -1,293 +1,957 @@
-using System;
-using System.IO;
-
-using SemWeb;
-using SemWeb.Filters;
-using SemWeb.Stores;
-using SemWeb.Util;
-
-#if !DOTNET2
-using System.Collections;
-#else
-using System.Collections.Generic;
-#endif
-
-#if !DOTNET2
-using ResList = System.Collections.ICollection;
-using LitFilterMap = System.Collections.Hashtable;
-using LitFilterList = System.Collections.ArrayList;
-#else
-using ResList = System.Collections.Generic.ICollection<SemWeb.Resource>;
-using LitFilterMap = System.Collections.Generic.Dictionary<SemWeb.Variable,System.Collections.Generic.ICollection<SemWeb.LiteralFilter>>;
-using LitFilterList = System.Collections.Generic.List<SemWeb.LiteralFilter>;
-#endif
-
-namespace SemWeb.Query {
-
-	public struct QueryOptions {
-		public int Limit; // 0 means no limit, otherwise the maximum number of results to give
-		
-		#if !DOTNET2
-		public ICollection DistinguishedVariables; // if null, all variables are reported back in bindings; otherwise, a list of just the variables whose bindings are to be reported
-		public IDictionary VariableKnownValues; // a map from variables to lists of values that the variable must be drawn from
-		public IDictionary VariableLiteralFilters; // a map from variables to lists of literal value filters that its values must match
-		#else
-		public ICollection<Variable> DistinguishedVariables;
-		public IDictionary<Variable,ICollection<Resource>> VariableKnownValues;
-		public IDictionary<Variable,ICollection<LiteralFilter>> VariableLiteralFilters;
-		#endif
-		
-		public void AddDistinguishedVariable(Variable variable) {
-			if (DistinguishedVariables == null)
-				#if !DOTNET2
-				DistinguishedVariables = new ArrayList();
-				#else
-				DistinguishedVariables = new List<Variable>();
-				#endif
-			#if !DOTNET2
-			((IList)DistinguishedVariables).Add(variable);
-			#else
-			((IList<Variable>)DistinguishedVariables).Add(variable);
-			#endif
-		}
-		
-		public void SetVariableKnownValues(Variable variable, ResList knownValues) {
-			if (VariableKnownValues == null)
-			#if !DOTNET2
-				VariableKnownValues = new Hashtable();
-			#else
-				VariableKnownValues = new Dictionary<Variable,ICollection<Resource>>();
-			#endif
-			
-			VariableKnownValues[variable] = knownValues;
-		}
-		
-		public void AddLiteralFilter(Variable variable, LiteralFilter filter) {
-			if (VariableLiteralFilters == null)
-				VariableLiteralFilters = new LitFilterMap();
-			LitFilterList list = null;
-			#if DOTNET2
-			if (VariableLiteralFilters.ContainsKey(variable))
-			#endif
-				list = (LitFilterList)VariableLiteralFilters[variable];
-			if (list == null) {
-			 	list  = new LitFilterList();
-				VariableLiteralFilters[variable] = list;
-			}
-			list.Add(filter);
-		}
-		
-		internal QueryOptions Clone() {
-			QueryOptions ret = new QueryOptions();
-			ret.Limit = Limit;
-
-			#if !DOTNET2
-			if (DistinguishedVariables != null)
-				ret.DistinguishedVariables = new ArrayList(DistinguishedVariables);
-			if (VariableKnownValues != null) {
-				ret.VariableKnownValues = new Hashtable();
-				foreach (Variable v in VariableKnownValues.Keys)
-					ret.VariableKnownValues[v] = new ArrayList((ICollection)VariableKnownValues[v]);
-			}
-			if (VariableLiteralFilters != null) {
-				ret.VariableLiteralFilters = new Hashtable();
-				foreach (Variable v in VariableLiteralFilters.Keys)
-					ret.VariableLiteralFilters[v] = new ArrayList((ICollection)VariableLiteralFilters[v]);
-			}
-			#else
-			if (DistinguishedVariables != null)
-				ret.DistinguishedVariables = new List<Variable>(DistinguishedVariables);
-			if (VariableKnownValues != null) {
-				ret.VariableKnownValues = new Dictionary<Variable,ICollection<Resource>>();
-				foreach (Variable v in VariableKnownValues.Keys)
-					ret.VariableKnownValues[v] = new List<Resource>(VariableKnownValues[v]);
-			}
-			if (VariableLiteralFilters != null) {
-				ret.VariableLiteralFilters = new Dictionary<Variable,ICollection<LiteralFilter>>();
-				foreach (Variable v in VariableLiteralFilters.Keys)
-					ret.VariableLiteralFilters[v] = new List<LiteralFilter>(VariableLiteralFilters[v]);
-			}
-			#endif
-			
-			return ret;
-		}
-	}
-	
-	public struct MetaQueryResult {
-		public bool QuerySupported;
-		public bool[] NoData;
-		public bool[] IsDefinitive;
-	}
-	
-
-	public class QueryFormatException : ApplicationException {
-		public QueryFormatException(string message) : base(message) { }
-		public QueryFormatException(string message, Exception cause) : base(message, cause) { }
-	}
-
-	public class QueryExecutionException : ApplicationException {
-		public QueryExecutionException(string message) : base(message) { }
-		public QueryExecutionException(string message, Exception cause) : base(message, cause) { }
+using System;
+using System.Collections;
+using System.IO;
+
+using SemWeb;
+using SemWeb.Filters;
+using SemWeb.Stores;
+using SemWeb.Util;
+
+namespace SemWeb.Query {
+
+	public class QueryFormatException : ApplicationException {
+		public QueryFormatException(string message) : base(message) { }
+		public QueryFormatException(string message, Exception cause) : base(message, cause) { }
+	}
+
+	public class QueryExecutionException : ApplicationException {
+		public QueryExecutionException(string message) : base(message) { }
+		public QueryExecutionException(string message, Exception cause) : base(message, cause) { }
 	}
 	
 	public abstract class RdfFunction {
 		public abstract string Uri { get; }
 		public abstract Resource Evaluate(Resource[] args);	
 	
-	}
-
-	public abstract class Query {
-		int start = 0;
-		int limit = -1;
-		Entity queryMeta = null;
-		
-		public int ReturnStart { get { return start; } set { start = value; if (start < 0) start = 0; } }
-		
-		public int ReturnLimit { get { return limit; } set { limit = value; } }
-		
-		public Entity QueryMeta { get { return queryMeta; } set { queryMeta = value; } }
-		
-		public virtual string MimeType {
-			get {
-				return SparqlXmlQuerySink.MimeType;
-			}
-			set {
-				throw new NotSupportedException();
-			}
-		}
-		
-		public virtual void Run(SelectableSource source, TextWriter output) {
-			Run(source, new SparqlXmlQuerySink(output));
-		}
-
-		public abstract void Run(SelectableSource source, QueryResultSink resultsink);
-
-		public abstract string GetExplanation();
-	}
-
-	public abstract class QueryResultSink {
-		public virtual void Init(Variable[] variables) {
-		}
-		
-		public abstract bool Add(VariableBindings result);
-
-		public virtual void Finished() {
-		}
-		
-		public virtual void AddComments(string comments) {
-		}
-	}
-	
-	public class QueryResultBuffer : QueryResultSink
-	#if !DOTNET2
-	, IEnumerable
-	#else
-	, IEnumerable<VariableBindings>
-	#endif
-	{
-		Variable[] variables;
-
-		#if !DOTNET2
-		ArrayList bindings = new ArrayList();
-		ArrayList comments = new ArrayList();
-		#else
-		List<VariableBindings> bindings = new List<VariableBindings>();
-		List<string> comments = new List<string>();
-		#endif
-		
-
-		public override void Init(Variable[] variables) {
-			this.variables = new Variable[variables.Length];
-			variables.CopyTo(this.variables, 0);
-		}
-
-		public override bool Add(VariableBindings result) {
-			bindings.Add(result);
-			return true;
-		}
-		
-		public override void AddComments(string comment) {
-			comments.Add(comment);
-		}
-		
-		public Variable[] Variables { get { return variables; } }
-
-		#if !DOTNET2
-		public IList Bindings { get { return bindings; } }
-		public IList Comments { get { return comments; } }
-		#else
-		public List<VariableBindings> Bindings { get { return bindings; } }
-		public List<string> Comments { get { return comments; } }
-		#endif
-		
-		System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
-			return Bindings.GetEnumerator();
-		}
-		#if DOTNET2
-		IEnumerator<VariableBindings> IEnumerable<VariableBindings>.GetEnumerator() {
-			return Bindings.GetEnumerator();
-		}
-		#endif
-	}
-	
-	public class VariableBindings {
-		Variable[] vars;
-		Resource[] vals;
-
-		public VariableBindings(Variable[] vars, Resource[] vals) {
-			this.vars = vars;
-			this.vals = vals;
-			if (vars.Length != vals.Length) throw new ArgumentException("Arrays do not have the same length.");
-		}
-		
-		public int Count { get { return vars.Length; } }
-		
-		#if !DOTNET2
-		public Variable[] Variables { get { return vars; } }
-		public Resource[] Values { get { return vals; } }
-		#else
-		public IList<Variable> Variables { get { return vars; } }
-		public IList<Resource> Values { get { return vals; } }
-		#endif
-		
-		public Resource this[Variable variable] {
-			get {
-				for (int i = 0; i < vars.Length; i++)
-					if (vars[i] == variable)
-						return vals[i];
-				throw new ArgumentException();
-			}
-		}
-
-		public Resource this[string variableName] {
-			get {
-				for (int i = 0; i < vars.Length; i++)
-					if (vars[i].LocalName != null && vars[i].LocalName == variableName)
-						return vals[i];
-				throw new ArgumentException();
-			}
-		}
-		
-		public Statement Substitute(Statement template) {
-			// This may throw an InvalidCastException if a variable binds
-			// to a literal but was used as the subject, predicate, or meta
-			// of the template.
-			for (int i = 0; i < vars.Length; i++) {
-				if (vars[i] == template.Subject) template = new Statement((Entity)vals[i], template.Predicate, template.Object, template.Meta);
-				if (vars[i] == template.Predicate) template = new Statement(template.Subject, (Entity)vals[i], template.Object, template.Meta);
-				if (vars[i] == template.Object) template = new Statement(template.Subject, template.Predicate, vals[i], template.Meta);
-				if (vars[i] == template.Meta) template = new Statement(template.Subject, template.Predicate, template.Object, (Entity)vals[i]);
-			}
-			return template;
-		}
-		
-		public override string ToString() {
-			String ret = "";
-			for (int i = 0; i < vars.Length; i++) {
-				ret += vars[i] + "=>" + vals[i] + "; ";
-			}
-			return ret;
-		}
-	}
-}
-
+	}
+
+	public abstract class Query {
+		int start = 0;
+		int limit = -1;
+		Entity queryMeta = null;
+		
+		public int ReturnStart { get { return start; } set { start = value; if (start < 0) start = 0; } }
+		
+		public int ReturnLimit { get { return limit; } set { limit = value; } }
+		
+		public Entity QueryMeta { get { return queryMeta; } set { queryMeta = value; } }
+		
+		public virtual void Run(SelectableSource source, TextWriter output) {
+			Run(source, new SparqlXmlQuerySink(output));
+		}
+
+		public abstract void Run(SelectableSource source, QueryResultSink resultsink);
+
+		public abstract string GetExplanation();
+	}
+
+	public class GraphMatch : Query {
+		// Setup information
+	
+		ArrayList setupVariablesDistinct = new ArrayList();
+		ArrayList setupValueFilters = new ArrayList();
+		ArrayList setupStatements = new ArrayList();
+		ArrayList setupOptionalStatements = new ArrayList();
+		
+		// Query model information
+		
+		bool init = false;
+		object sync = new object();
+		Variable[] variables;
+		SemWeb.Variable[] variableEntities;
+		QueryStatement[][] statements;
+		ArrayList novariablestatements = new ArrayList();
+		
+		// contains functional and inverse functional properties
+		ResSet fps = new ResSet(),
+		          ifps = new ResSet();
+		
+		private struct Variable {
+			public SemWeb.Variable Entity;
+			public LiteralFilter[] Filters;
+		}
+		
+		private struct VarOrAnchor {
+			public bool IsVariable;
+			public int VarIndex;
+			public Resource Anchor;
+			public Resource[] ArrayOfAnchor;
+			
+			public override string ToString() {
+				if (IsVariable)
+					return "?" + VarIndex;
+				else
+					return Anchor.ToString();
+			}
+			
+			public Resource[] GetValues(QueryResult union, bool entities) {
+				if (!IsVariable) {
+					if (entities)
+						return new Entity[] { (Entity)Anchor };
+					else
+						return ArrayOfAnchor;
+				} else {
+					if (union.Bindings[VarIndex] == null) return null;
+					Resource[] res = union.Bindings[VarIndex].ToArray();
+					if (!entities) return res;
+					
+					ArrayList ret = new ArrayList();
+					foreach (Resource r in res)
+						if (r is Entity)
+							ret.Add(r);
+					return (Entity[])ret.ToArray(typeof(Entity));
+				}
+			}
+		}
+		
+		private class QueryStatement { // class because of use with IComparer
+			public bool Optional;
+			public VarOrAnchor 
+				Subject,
+				Predicate,
+				Object;
+			
+			public int NumVars() {
+				return (Subject.IsVariable ? 1 : 0)
+					+ (Predicate.IsVariable ? 1 : 0)
+					+ (Object.IsVariable ? 1 : 0);
+			}
+			
+			public override string ToString() {
+				return Subject + " " + Predicate + " " + Object;
+			}
+		}
+		
+		class QueryResult {
+			public ResSet[] Bindings;
+			public bool[] StatementMatched;
+			
+			public QueryResult(GraphMatch q) {
+				Bindings = new ResSet[q.variables.Length];
+				StatementMatched = new bool[q.statements.Length];
+			}
+			private QueryResult(int x, int y) {
+				Bindings = new ResSet[x];
+				StatementMatched = new bool[y];
+			}
+			public void Add(QueryStatement qs, Statement bs) {
+				if (qs.Subject.IsVariable) Add(qs.Subject.VarIndex, bs.Subject);
+				if (qs.Predicate.IsVariable) Add(qs.Predicate.VarIndex, bs.Predicate);
+				if (qs.Object.IsVariable) Add(qs.Object.VarIndex, bs.Object);
+			}
+			void Add(int varIndex, Resource binding) {
+				if (Bindings[varIndex] == null) Bindings[varIndex] = new ResSet();
+				Bindings[varIndex].Add(binding);
+			}
+			public void Clear(QueryStatement qs) {
+				if (qs.Subject.IsVariable && Bindings[qs.Subject.VarIndex] != null) Bindings[qs.Subject.VarIndex].Clear();
+				if (qs.Predicate.IsVariable && Bindings[qs.Predicate.VarIndex] != null) Bindings[qs.Predicate.VarIndex].Clear();
+				if (qs.Object.IsVariable && Bindings[qs.Object.VarIndex] != null) Bindings[qs.Object.VarIndex].Clear();
+			}
+			public void Set(QueryStatement qs, Statement bs) {
+				if (qs.Subject.IsVariable) Set(qs.Subject.VarIndex, bs.Subject);
+				if (qs.Predicate.IsVariable) Set(qs.Predicate.VarIndex, bs.Predicate);
+				if (qs.Object.IsVariable) Set(qs.Object.VarIndex, bs.Object);
+			}
+			void Set(int varIndex, Resource binding) {
+				if (Bindings[varIndex] == null) Bindings[varIndex] = new ResSet();
+				else Bindings[varIndex].Clear();
+				Bindings[varIndex].Add(binding);
+			}
+			public QueryResult Clone() {
+				QueryResult r = new QueryResult(Bindings.Length, StatementMatched.Length);
+				for (int i = 0; i < Bindings.Length; i++)
+					if (Bindings[i] != null)
+						r.Bindings[i] = Bindings[i].Clone();
+				for (int i = 0; i < StatementMatched.Length; i++)
+					r.StatementMatched[i] = StatementMatched[i];
+				return r;
+			}
+		}
+		
+		class BindingSet {
+			public ArrayList Results = new ArrayList();
+			public QueryResult Union;
+			
+			public BindingSet(GraphMatch q) {
+				Union = new QueryResult(q);
+			}
+		}
+		
+		public void MakeDistinct(BNode a, BNode b) {
+			SetupVariablesDistinct d = new SetupVariablesDistinct();
+			d.a = a;
+			d.b = b;
+			setupVariablesDistinct.Add(d);
+		}
+		
+		public void AddValueFilter(Entity entity, LiteralFilter filter) {
+			SetupValueFilter d = new SetupValueFilter();
+			d.a = entity;
+			d.b = filter;
+			setupValueFilters.Add(d);
+		}
+		
+		public void AddEdge(Statement filter) {
+			setupStatements.Add(filter);
+		}
+
+		public void AddOptionalEdge(Statement filter) {
+			setupOptionalStatements.Add(filter);
+		}
+		
+		private class SetupVariablesDistinct {
+			public Entity a, b;
+		}
+		private class SetupValueFilter {
+			public Entity a;
+			public LiteralFilter b;
+		}
+		
+		private void CheckInit() {
+			lock (sync) {
+				if (!init) {
+					Init();
+					init = true;
+				}
+			}
+		}
+
+		private static Entity qLimit = "http://purl.oclc.org/NET/rsquary/returnLimit";;
+		private static Entity qStart = "http://purl.oclc.org/NET/rsquary/returnStart";;
+		private static Entity qDistinctFrom = "http://purl.oclc.org/NET/rsquary/distinctFrom";;
+		private static Entity qOptional = "http://purl.oclc.org/NET/rsquary/optional";;
+		
+		public GraphMatch() {
+		}
+		
+		public GraphMatch(RdfReader query) :
+			this(new MemoryStore(query),
+				query.BaseUri == null ? null : new Entity(query.BaseUri)) {
+		}
+
+		public GraphMatch(Store queryModel) : this(queryModel, null) {
+		}
+		
+		private GraphMatch(Store queryModel, Entity queryNode) {
+			// Find the query options
+			if (queryNode != null) {
+				ReturnStart = GetIntOption(queryModel, queryNode, qStart);
+				ReturnLimit = GetIntOption(queryModel, queryNode, qLimit);
+			}
+
+			// Search the query for 'distinct' predicates between variables.
+			foreach (Statement s in queryModel.Select(new Statement(null, qDistinctFrom, null))) {
+				if (!(s.Object is BNode)) continue;
+				MakeDistinct((BNode)s.Subject, (BNode)s.Object);
+			}
+			
+			// Add all statements except the query predicates and value filters into a
+			// new store with just the statements relevant to the search.
+			foreach (Statement s in queryModel.Select(Statement.All)) {
+				if (IsQueryPredicate(s.Predicate)) continue;
+				
+				/*if (s.Predicate.Uri != null && extraValueFilters != null && extraValueFilters.Contains(s.Predicate.Uri)) {
+					ValueFilterFactory f = (ValueFilterFactory)extraValueFilters[s.Predicate.Uri];
+					AddValueFilter(s.Subject, f.GetValueFilter(s.Predicate.Uri, s.Object));
+					continue;
+				} else {
+					ValueFilter f = ValueFilter.GetValueFilter(s.Predicate, s.Object);
+					if (f != null) {
+						AddValueFilter(s.Subject, f);
+						continue;
+					}
+				}*/
+				
+				if (s.Meta == Statement.DefaultMeta)
+					AddEdge(s);
+				else if (queryNode != null && queryModel.Contains(new Statement(queryNode, qOptional, s.Meta)))
+					AddOptionalEdge(s);
+			}
+		}
+		
+		private int GetIntOption(Store queryModel, Entity query, Entity predicate) {
+			Resource[] rr = queryModel.SelectObjects(query, predicate);
+			if (rr.Length == 0) return -1;
+			Resource r = rr[0];
+			if (r == null || !(r is Literal)) return -1;
+			try {
+				return int.Parse(((Literal)r).Value);
+			} catch (Exception e) {
+				return -1;
+			}
+		}		
+
+		private bool IsQueryPredicate(Entity e) {
+			if (e == qDistinctFrom) return true;
+			if (e == qLimit) return true;
+			if (e == qStart) return true;
+			if (e == qOptional) return true;
+			return false;
+		}
+		
+		public override string GetExplanation() {
+			CheckInit();
+			string ret = "Query:\n";
+			foreach (Statement s in novariablestatements)
+				ret += " Check: " + s + "\n";
+			foreach (QueryStatement[] sgroup in statements) {
+				ret += " ";
+				if (sgroup.Length != 1)
+					ret += "{";
+				foreach (QueryStatement s in sgroup) {
+					ret += s.ToString();
+					if (s.Optional) ret += " (Optional)";
+					if (sgroup.Length != 1)
+						ret += " & ";
+				}
+				if (sgroup.Length != 1)
+					ret += "}";
+				ret += "\n";
+			}
+			return ret;
+		}
+		
+		public override void Run(SelectableSource targetModel, QueryResultSink result) {
+			CheckInit();
+			
+			foreach (Statement s in novariablestatements)
+				if (!targetModel.Contains(s))
+					return;
+			
+			VariableBinding[] finalbindings = new VariableBinding[variables.Length];
+			for (int i = 0; i < variables.Length; i++)
+				finalbindings[i].Variable = variableEntities[i];
+			
+			result.Init(finalbindings, true, false);
+			
+			Debug("Begnning Query");
+			
+			BindingSet bindings = new BindingSet(this);
+			for (int group = 0; group < statements.Length; group++) {
+				bool ret = Query(group, bindings, targetModel);
+				if (!ret) {
+					// A false return value indicates the query
+					// certainly failed -- a non-optional statement
+					// failed to match at all.
+					result.Finished();
+					return;
+				}
+			}
+
+			int ctr = -1;
+			foreach (QueryResult r in bindings.Results) {
+				Permutation permutation = new Permutation(r.Bindings);
+				do {
+					ctr++;
+					if (ctr < ReturnStart) continue;
+					for (int i = 0; i < variables.Length; i++)
+						finalbindings[i].Target = permutation[i];
+					result.Add(finalbindings);
+					if (ReturnLimit != -1 && ctr == ReturnStart+ReturnLimit) break;	
+				} while (permutation.Next());
+				if (ReturnLimit != -1 && ctr == ReturnStart+ReturnLimit) break;	
+			}
+
+			
+			result.Finished();
+		}
+		
+		class Permutation {
+			public int[] index;
+			public Resource[][] values;
+			
+			public Resource this[int i] {
+				get {
+					return values[i][index[i]];
+				}
+			}
+			
+			public Permutation(ResSet[] bindings) {
+				index = new int[bindings.Length];
+				values = new Resource[bindings.Length][];
+				for (int i = 0; i < bindings.Length; i++) {
+					values[i] = new Resource[bindings[i] == null ? 1 : bindings[i].Count];
+					if (bindings[i] != null) {
+						int ctr = 0;
+						foreach (Resource r in bindings[i])
+							values[i][ctr++] = r;
+					}
+				}
+			}
+			public bool Next() {
+				for (int i = 0; i < index.Length; i++) {
+					index[i]++;
+					if (index[i] < values[i].Length) break;
+					
+					index[i] = 0;
+					if (i == index.Length-1) return false;
+				}
+				return true;
+			}
+		}
+		
+		private void Debug(string message) {
+			//Console.Error.WriteLine(message);
+		}
+		
+		class BindingEnumerator {
+			IEnumerator[] loops = new IEnumerator[3];
+			int loop = 0;
+			
+			public BindingEnumerator(QueryStatement qs, QueryResult bindings) {
+				loops[0] = GetBindings(qs.Subject, bindings);
+				loops[1] = GetBindings(qs.Predicate, bindings);
+				loops[2] = GetBindings(qs.Object, bindings);
+			}
+			
+			public bool MoveNext(out Entity s, out Entity p, out Resource o) {
+				while (true) {
+					bool b = loops[loop].MoveNext();
+					if (!b) {
+						 if (loop == 0) { s = null; p = null; o = null; return false; }
+						 loops[loop].Reset();
+						 loop--;
+						 continue;
+					}
+					
+					if (loop <= 1) {
+						object obj = loops[loop].Current;
+						if (obj != null && !(obj is Entity)) continue;
+					}
+					
+					if (loop < 2) { loop++; continue; }
+					
+					s = loops[0].Current as Entity; 
+					p = loops[1].Current as Entity; 
+					o = loops[2].Current as Resource;
+					return true; 
+				}
+			}
+		}
+
+		private bool Query(int groupindex, BindingSet bindings, SelectableSource targetModel) {
+			QueryStatement[] group = statements[groupindex];
+			
+			QueryStatement qs = group[0];
+			
+			int numMultiplyBound = IsMultiplyBound(qs.Subject, bindings)
+				+ IsMultiplyBound(qs.Predicate, bindings)
+				+ IsMultiplyBound(qs.Object, bindings);
+			
+			if (numMultiplyBound >= 1) {
+				// If there is one or more multiply-bound variable,
+				// then we need to iterate through the permutations
+				// of the variables in the statement.
+				
+				Debug(qs.ToString() + " Something Multiply Bound");
+				
+				MemoryStore matches = new MemoryStore();
+				targetModel.Select(
+					new SelectFilter(
+						(Entity[])qs.Subject.GetValues(bindings.Union, true),
+						(Entity[])qs.Predicate.GetValues(bindings.Union, true),
+						qs.Object.GetValues(bindings.Union, false),
+						QueryMeta == null ? null : new Entity[] { QueryMeta }
+						),
+					new ClearMetaDupCheck(matches));
+				
+				Debug("\t" + matches.StatementCount + " Matches");
+				
+				if (matches.StatementCount == 0) {
+					// This statement doesn't match any of
+					// the existing bindings.  If this was
+					// optional, preserve the bindings.
+					return qs.Optional;
+				}
+				
+				// We need to preserve the pairings of
+				// the multiply bound variable with the matching
+				// statements.
+				
+				ArrayList newbindings = new ArrayList();
+				
+				if (!qs.Optional) bindings.Union.Clear(qs);
+				
+				foreach (QueryResult binding in bindings.Results) {
+					// Break apart the permutations in this binding.
+					BindingEnumerator enumer2 = new BindingEnumerator(qs, binding);
+					Entity s, p;
+					Resource o;
+					while (enumer2.MoveNext(out s, out p, out o)) {
+						// Get the matching statements from the union query
+						Statement bs = new Statement(s, p, o);
+						MemoryStore innermatches = matches.Select(bs).Load();
+						
+						// If no matches, the binding didn't match the filter.
+						if (innermatches.StatementCount == 0) {
+							if (qs.Optional) {
+								// Preserve the binding.
+								QueryResult bc = binding.Clone();
+								bc.Set(qs, bs);
+								newbindings.Add(bc);
+								continue;
+							} else {
+								// Toss out the binding.
+								continue;
+							}
+						}
+						
+						for (int si = 0; si < innermatches.StatementCount; si++) {
+							Statement m = innermatches[si];
+							if (!MatchesFilters(m, qs, targetModel)) {
+								if (qs.Optional) {
+									QueryResult bc = binding.Clone();
+									bc.Set(qs, bs);
+									newbindings.Add(bc);
+								}
+								continue;
+							}
+							bindings.Union.Add(qs, m);
+							
+							QueryResult r = binding.Clone();
+							r.Set(qs, m);
+							r.StatementMatched[groupindex] = true;
+							newbindings.Add(r);
+						}
+					}
+				}
+				
+				bindings.Results = newbindings;
+				
+			} else {
+				// There are no multiply bound variables, but if
+				// there are more than two unbound variables,
+				// we need to be sure to preserve the pairings
+				// of the matching values.
+			
+				int numUnbound = IsUnbound(qs.Subject, bindings)
+					+ IsUnbound(qs.Predicate, bindings)
+					+ IsUnbound(qs.Object, bindings);
+					
+				bool sunbound = IsUnbound(qs.Subject, bindings) == 1;
+				bool punbound = IsUnbound(qs.Predicate, bindings) == 1;
+				bool ounbound = IsUnbound(qs.Object, bindings) == 1;
+				
+				Statement s = GetStatement(qs, bindings);
+				
+				// If we couldn't get a statement out of this,
+				// then if this was not an optional filter,
+				// fail.  If this was optional, don't change
+				// the bindings any. 
+				if (s == StatementFailed) return qs.Optional;
+				
+				if (numUnbound == 0) {
+					Debug(qs.ToString() + " All bound");
+					
+					// All variables are singly bound already.
+					// We can just test if the statement exists.
+					if (targetModel.Contains(s)) {
+						// Mark each binding that it matched this statement.
+						foreach (QueryResult r in bindings.Results)
+							r.StatementMatched[groupindex] = true;
+					} else {
+						return qs.Optional;
+					}
+				
+				} else if (numUnbound == 1) {
+					Debug(qs.ToString() + " 1 Unbound");
+				
+					// There is just one unbound variable.  The others
+					// are not multiply bound, so they must be uniquely
+					// bound (but they may not be bound in all results).
+					// Run a combined select to find all possible values
+					// of the unbound variable at once, and set these to
+					// be the values of the variable for matching results.
+					
+					ResSet values = new ResSet();
+					MemoryStore ms = new MemoryStore();
+					targetModel.Select(s, ms);
+					for (int si = 0; si < ms.StatementCount; si++) {
+						Statement match = ms[si];
+						if (!MatchesFilters(match, qs, targetModel)) continue;
+						if (sunbound) values.Add(match.Subject);
+						if (punbound) values.Add(match.Predicate);
+						if (ounbound) values.Add(match.Object);
+					}
+					
+					Debug("\t" + values.Count + " matches");
+					
+					if (values.Count == 0)
+						return qs.Optional;
+						
+					int varIndex = -1;
+					if (sunbound) varIndex = qs.Subject.VarIndex;
+					if (punbound) varIndex = qs.Predicate.VarIndex;
+					if (ounbound) varIndex = qs.Object.VarIndex;
+					
+					if (bindings.Results.Count == 0)
+						bindings.Results.Add(new QueryResult(this));
+					
+					bindings.Union.Bindings[varIndex] = new ResSet();
+					foreach (Resource r in values)
+						bindings.Union.Bindings[varIndex].Add(r);
+						
+					foreach (QueryResult r in bindings.Results) {
+						// Check that the bound variables are bound in this result.
+						// If it is bound, it will be bound to the correct resource,
+						// but it might not be bound at all if an optional statement
+						// failed to match -- in which case, don't modify the binding.
+						if (qs.Subject.IsVariable && !sunbound && r.Bindings[qs.Subject.VarIndex] == null) continue;
+						if (qs.Predicate.IsVariable && !punbound && r.Bindings[qs.Predicate.VarIndex] == null) continue;
+						if (qs.Object.IsVariable && !ounbound && r.Bindings[qs.Object.VarIndex] == null) continue;
+					
+						r.Bindings[varIndex] = values;
+						r.StatementMatched[groupindex] = true;
+					}
+					
+				} else {
+					// There are two or more unbound variables, the
+					// third variable being uniquely bound, if bound.
+					// Keep track of the pairing of unbound variables.
+					
+					if (numUnbound == 3)
+						throw new QueryExecutionException("Query would select all statements in the store.");
+					
+					Debug(qs.ToString() + " 2 or 3 Unbound");
+				
+					if (bindings.Results.Count == 0)
+						bindings.Results.Add(new QueryResult(this));
+						
+					ArrayList newbindings = new ArrayList();
+					MemoryStore ms = new MemoryStore();
+					targetModel.Select(s, ms);
+					for (int si = 0; si < ms.StatementCount; si++) {
+						Statement match = ms[si];
+						if (!MatchesFilters(match, qs, targetModel)) continue;
+						bindings.Union.Add(qs, match);
+						foreach (QueryResult r in bindings.Results) {
+							if (numUnbound == 2) {
+								// Check that the bound variable is bound in this result.
+								// If it is bound, it will be bound to the correct resource,
+								// but it might not be bound at all if an optional statement
+								// failed to match -- in which case, preserve the binding if
+								// this was an optional statement.
+								bool matches = true;
+								if (qs.Subject.IsVariable && !sunbound && r.Bindings[qs.Subject.VarIndex] == null) matches = false;
+								if (qs.Predicate.IsVariable && !punbound && r.Bindings[qs.Predicate.VarIndex] == null) matches = false;
+								if (qs.Object.IsVariable && !ounbound && r.Bindings[qs.Object.VarIndex] == null) matches = false;
+								if (!matches) {
+									if (qs.Optional)
+										newbindings.Add(r);
+									continue;
+								}
+							}
+						
+							QueryResult r2 = r.Clone();
+							r2.Add(qs, match);
+							r2.StatementMatched[groupindex] = true;
+							newbindings.Add(r2);
+						}
+					}
+					if (newbindings.Count == 0)
+						return qs.Optional; // don't clear out bindings if this was optional and it failed
+					bindings.Results = newbindings;
+				}
+			}
+			
+			return true;
+		}
+		
+		static Resource[] ResourceArrayNull = new Resource[] { null };
+		
+		private static IEnumerator GetBindings(VarOrAnchor e, QueryResult bindings) {
+			if (!e.IsVariable) {
+				if (e.ArrayOfAnchor == null)
+					e.ArrayOfAnchor = new Resource[] { e.Anchor };
+				return e.ArrayOfAnchor.GetEnumerator();
+			}
+			if (bindings.Bindings[e.VarIndex] == null) return ResourceArrayNull.GetEnumerator();
+			return bindings.Bindings[e.VarIndex].Items.GetEnumerator();
+		}
+		private int IsMultiplyBound(VarOrAnchor e, BindingSet bindings) {
+			if (!e.IsVariable) return 0;
+			if (bindings.Union.Bindings[e.VarIndex] == null) return 0;
+			if (bindings.Union.Bindings[e.VarIndex].Items.Count == 1) return 0;
+			return 1;
+		}
+		private int IsUnbound(VarOrAnchor e, BindingSet bindings) {
+			if (!e.IsVariable) return 0;
+			if (bindings.Union.Bindings[e.VarIndex] == null) return 1;
+			return 0;
+		}
+		
+		private Resource GetUniqueBinding(VarOrAnchor e, BindingSet bindings) {
+			if (!e.IsVariable) return e.Anchor;
+			if (bindings.Union.Bindings[e.VarIndex] == null || bindings.Union.Bindings[e.VarIndex].Count == 0) return null;
+			if (bindings.Union.Bindings[e.VarIndex].Count > 1) throw new Exception();
+			foreach (Resource r in bindings.Union.Bindings[e.VarIndex].Items)
+				return r;
+			throw new Exception();
+		}
+		
+		Statement StatementFailed = new Statement(null, null, null);
+		
+		private Statement GetStatement(QueryStatement sq, BindingSet bindings) {
+			Resource s = GetUniqueBinding(sq.Subject, bindings);
+			Resource p = GetUniqueBinding(sq.Predicate, bindings);
+			Resource o = GetUniqueBinding(sq.Object, bindings);
+			if (s is Literal || p is Literal) return StatementFailed;
+			return new Statement((Entity)s, (Entity)p, o, QueryMeta);
+		}
+		
+		bool MatchesFilters(Statement s, QueryStatement q, SelectableSource targetModel) {
+			return MatchesFilters(s.Subject, q.Subject, targetModel)
+				&& MatchesFilters(s.Predicate, q.Predicate, targetModel)
+				&& MatchesFilters(s.Object, q.Object, targetModel);
+		}
+		
+		bool MatchesFilters(Resource e, VarOrAnchor var, SelectableSource targetModel) {
+			if (!var.IsVariable) return true;
+			/*foreach (ValueFilter f in variables[var.VarIndex].Filters) {
+				if (!f.Filter(e, targetModel)) return false;
+			}*/
+			return true;
+		}
+		
+		class ClearMetaDupCheck : StatementSink {
+			MemoryStore m;
+			public ClearMetaDupCheck(MemoryStore m) { this.m = m; }
+			public bool Add(Statement s) {
+				// remove meta information
+				s = new Statement(s.Subject, s.Predicate, s.Object);
+				if (!m.Contains(s))
+					m.Add(s);
+				return true;
+			}
+		}
+		
+		private void Init() {
+			// Get the list of variables, which is the set
+			// of anonymous nodes in the statements to match.
+			ArrayList setupVariables = new ArrayList();
+			
+			if (setupStatements.Count == 0)
+				throw new QueryFormatException("A query must have at least one non-optional statement.");
+			
+			foreach (Statement s in setupStatements) {
+				InitAnonVariable(s.Subject, setupVariables);
+				InitAnonVariable(s.Predicate, setupVariables);
+				InitAnonVariable(s.Object, setupVariables);
+			}
+			foreach (Statement s in setupOptionalStatements) {
+				InitAnonVariable(s.Subject, setupVariables);
+				InitAnonVariable(s.Predicate, setupVariables);
+				InitAnonVariable(s.Object, setupVariables);
+			}
+		
+			// Set up the variables array.
+			variables = new Variable[setupVariables.Count];
+			variableEntities = new SemWeb.Variable[variables.Length];
+			Hashtable varIndex = new Hashtable();
+			for (int i = 0; i < variables.Length; i++) {
+				variables[i].Entity = (SemWeb.Variable)setupVariables[i];
+				variableEntities[i] = variables[i].Entity;
+				varIndex[variables[i].Entity] = i;
+				
+				ArrayList filters = new ArrayList();
+				foreach (SetupValueFilter filter in setupValueFilters) {
+					if (filter.a == variables[i].Entity)
+						filters.Add(filter.b);
+				}
+				
+				//variables[i].Filters = (ValueFilter[])filters.ToArray(typeof(ValueFilter));
+			}
+			
+			// Set up the statements
+			ArrayList statements = new ArrayList();
+			foreach (Statement st in setupStatements)
+				InitSetStatement(st, statements, varIndex, false);
+			foreach (Statement st in setupOptionalStatements)
+				InitSetStatement(st, statements, varIndex, true);
+			
+			// Order the statements in the most efficient order
+			// for the recursive query.
+			Hashtable setVars = new Hashtable();
+			ArrayList sgroups = new ArrayList();
+			while (statements.Count > 0) {
+				QueryStatement[] group = InitBuildNode(statements, setVars);
+				sgroups.Add(group);
+				foreach (QueryStatement qs in group) {
+					if (qs.Subject.IsVariable) setVars[qs.Subject.VarIndex] = setVars;
+					if (qs.Predicate.IsVariable) setVars[qs.Predicate.VarIndex] = setVars;
+					if (qs.Object.IsVariable) setVars[qs.Object.VarIndex] = setVars;
+				}
+			}
+			
+			this.statements = (QueryStatement[][])sgroups.ToArray(typeof(QueryStatement[]));
+		}
+		
+		private void InitAnonVariable(Resource r, ArrayList setupVariables) {
+			if (r is SemWeb.Variable)
+				setupVariables.Add(r);
+		}
+		
+		private void InitSetStatement(Statement st, ArrayList statements, Hashtable varIndex, bool optional) {
+			QueryStatement qs = new QueryStatement();
+			
+			InitSetStatement(st.Subject, ref qs.Subject, varIndex);
+			InitSetStatement(st.Predicate, ref qs.Predicate, varIndex);
+			InitSetStatement(st.Object, ref qs.Object, varIndex);
+			
+			qs.Optional = optional;
+			
+			// If this statement has no variables, add it to a separate list.
+			if (!qs.Subject.IsVariable && !qs.Predicate.IsVariable && !qs.Object.IsVariable)
+				novariablestatements.Add(st);
+			else
+				statements.Add(qs);
+		}
+		
+		private void InitSetStatement(Resource ent, ref VarOrAnchor st, Hashtable varIndex) {
+			if (!varIndex.ContainsKey(ent)) {
+				st.IsVariable = false;
+				st.Anchor = ent;
+			} else {
+				st.IsVariable = true;
+				st.VarIndex = (int)varIndex[ent];
+			}
+		}
+		
+		private class QueryStatementComparer : IComparer {
+			Hashtable setVars;
+			ResSet fps, ifps;
+			
+			public QueryStatementComparer(Hashtable setVars, ResSet fps, ResSet ifps) {
+				this.setVars = setVars;
+				this.fps = fps;
+				this.ifps = ifps;
+			}
+		
+			int IComparer.Compare(object a, object b) {
+				return Compare((QueryStatement)a, (QueryStatement)b);
+			}
+			
+			public int Compare(QueryStatement a, QueryStatement b) {
+				int optional = a.Optional.CompareTo(b.Optional);
+				if (optional != 0) return optional;
+				
+				int numvars = NumVars(a).CompareTo(NumVars(b));
+				if (numvars != 0) return numvars;
+				
+				int complexity = Complexity(a).CompareTo(Complexity(b));
+				return complexity;
+			}
+			
+			private int NumVars(QueryStatement s) {
+				int ret = 0;
+				if (s.Subject.IsVariable && !setVars.ContainsKey(s.Subject.VarIndex))
+					ret++;
+				if (s.Predicate.IsVariable && !setVars.ContainsKey(s.Predicate.VarIndex))
+					ret++;
+				if (s.Object.IsVariable && !setVars.ContainsKey(s.Object.VarIndex))
+					ret++;
+				return ret;
+			}
+			
+			private int Complexity(QueryStatement s) {
+				if (s.Predicate.IsVariable) return 2;
+				if ((!s.Subject.IsVariable || setVars.ContainsKey(s.Subject.VarIndex))
+					&& fps.Contains(s.Predicate.Anchor))
+					return 0;
+				if ((!s.Object.IsVariable || setVars.ContainsKey(s.Object.VarIndex))
+					&& ifps.Contains(s.Predicate.Anchor))
+					return 0;
+				return 1;
+			}
+		}
+		
+		private QueryStatement[] InitBuildNode(ArrayList statements, Hashtable setVars) {
+			// Get the best statements to consider
+			// Because we can consider statements in groups, we need
+			// a list of lists.
+			QueryStatementComparer comparer = new QueryStatementComparer(setVars, fps, ifps);
+			ArrayList considerations = new ArrayList();
+			for (int i = 0; i < statements.Count; i++) {
+				QueryStatement next = (QueryStatement)statements[i];
+				int comp = 1;
+				if (considerations.Count > 0) {
+					QueryStatement curcomp = (QueryStatement) ((ArrayList)considerations[0])[0];
+					comp = comparer.Compare(curcomp, next);
+				}
+				
+				if (comp < 0) // next is worse than current
+					continue;
+				
+				if (comp > 0) // clear out worse possibilities
+					considerations.Clear();
+				
+				ArrayList group = new ArrayList();
+				group.Add(next);
+				considerations.Add(group);
+			}
+			
+			// Pick the group with the most number of statements.
+			ArrayList bestgroup = null;
+			foreach (ArrayList g in considerations) {
+				if (bestgroup == null || bestgroup.Count < g.Count)
+					bestgroup = g;
+			}
+			
+			foreach (QueryStatement qs in bestgroup)
+				statements.Remove(qs);
+			
+			return (QueryStatement[])bestgroup.ToArray(typeof(QueryStatement));
+		}
+
+	}
+	
+	public abstract class QueryResultSink {
+		public virtual void Init(VariableBinding[] variables, bool distinct, bool ordered) {
+		}
+		
+		public abstract bool Add(VariableBinding[] result);
+
+		public virtual void Finished() {
+		}
+		
+		public virtual void AddComments(string comments) {
+		}
+	}
+	
+	internal class QueryResultBufferSink : QueryResultSink {
+		public ArrayList Bindings = new ArrayList();
+		public override bool Add(VariableBinding[] result) {
+			Bindings.Add(result.Clone());
+			return true;
+		}
+	}
+
+	public struct VariableBinding {
+		Variable v;
+		Resource t;
+		
+		public VariableBinding(Variable variable, Resource target) {
+			v = variable;
+			t = target;
+		}
+		
+		public Variable Variable { get { return v; } set { v = value; } }
+		public string Name { get { return v.LocalName; } }
+		public Resource Target { get { return t; } set { t = value; } }
+
+		public static Statement Substitute(VariableBinding[] variables, Statement template) {
+			// This may throw an InvalidCastException if a variable binds
+			// to a literal but was used as the subject, predicate, or meta
+			// of the template.
+			foreach (VariableBinding v in variables) {
+				if (v.Variable == template.Subject) template = new Statement((Entity)v.Target, template.Predicate, template.Object, template.Meta);
+				if (v.Variable == template.Predicate) template = new Statement(template.Subject, (Entity)v.Target, template.Object, template.Meta);
+				if (v.Variable == template.Object) template = new Statement(template.Subject, template.Predicate, v.Target, template.Meta);
+				if (v.Variable == template.Meta) template = new Statement(template.Subject, template.Predicate, template.Object, (Entity)v.Target);
+			}
+			return template;
+		}
+	}
+}
+

Modified: trunk/semweb/RDFS.cs
==============================================================================
--- trunk/semweb/RDFS.cs	(original)
+++ trunk/semweb/RDFS.cs	Fri May 16 18:52:24 2008
@@ -5,23 +5,14 @@
 using SemWeb.Stores;
 using SemWeb.Util;
 
-#if !DOTNET2
-using ResourceList = System.Collections.ICollection;
-using VarKnownValuesType = System.Collections.Hashtable;
-#else
-using ResourceList = System.Collections.Generic.ICollection<SemWeb.Resource>;
-using VarKnownValuesType = System.Collections.Generic.Dictionary<SemWeb.Variable,System.Collections.Generic.ICollection<SemWeb.Resource>>;
-#endif
-
 namespace SemWeb.Inference {
 
-	public class RDFS : Reasoner {
+	public class RDFS : SelectableSource, SupportsPersistableBNodes, IDisposable {
 		static readonly Entity type = NS.RDF + "type";
 		static readonly Entity subClassOf = NS.RDFS + "subClassOf";
 		static readonly Entity subPropertyOf = NS.RDFS + "subPropertyOf";
 		static readonly Entity domain = NS.RDFS + "domain";
 		static readonly Entity range = NS.RDFS + "range";
-		static readonly Entity rdfsresource = NS.RDFS + "Resource";
 	
 		// Each of these hashtables relates an entity
 		// to a ResSet of other entities, including itself.
@@ -38,52 +29,61 @@
 		Hashtable domainof = new Hashtable();
 		Hashtable rangeof = new Hashtable();
 		
+		SelectableSource data;
+		
 		StatementSink schemasink;
 		
-		public RDFS() {
+		public RDFS(SelectableSource data) {
+			this.data = data;
 			schemasink = new SchemaSink(this);
 		}
 		
-		public RDFS(StatementSource schema) : this() {
+		public RDFS(StatementSource schema, SelectableSource data)
+		: this(data) {
 			LoadSchema(schema);
 		}
 		
+		void IDisposable.Dispose() {
+			if (data is IDisposable)
+				((IDisposable)data).Dispose();
+		}
+		
 		public StatementSink Schema { get { return schemasink; } }
 		
+		string SupportsPersistableBNodes.GetStoreGuid() { if (data is SupportsPersistableBNodes) return ((SupportsPersistableBNodes)data).GetStoreGuid(); return null; }
+		
+		string SupportsPersistableBNodes.GetNodeId(BNode node) { if (data is SupportsPersistableBNodes) return ((SupportsPersistableBNodes)data).GetNodeId(node); return null; }
+		
+		BNode SupportsPersistableBNodes.GetNodeFromId(string persistentId) { if (data is SupportsPersistableBNodes) return ((SupportsPersistableBNodes)data).GetNodeFromId(persistentId); return null; }
+
 		class SchemaSink : StatementSink {
 			RDFS rdfs;
 			public SchemaSink(RDFS parent) { rdfs = parent; }
-			bool StatementSink.Add(Statement s) { rdfs.AddAxiom(s); return true; }
+			bool StatementSink.Add(Statement s) { rdfs.Add(s); return true; }
 		}
 		
-		void AddAxiom(Statement schemastatement) {
-			if (schemastatement.Predicate == subClassOf && schemastatement.Object is Entity) {
-				AddRelation(schemastatement.Subject, (Entity)schemastatement.Object, superclasses, subclasses);
-				AddRelation(schemastatement.Subject, rdfsresource, superclasses, subclasses);
-				AddRelation((Entity)schemastatement.Object, rdfsresource, superclasses, subclasses);
-			}
+		void Add(Statement schemastatement) {
+			if (schemastatement.Predicate == subClassOf && schemastatement.Object is Entity)
+				AddRelation(schemastatement.Subject, (Entity)schemastatement.Object, superclasses, subclasses, true);
 			if (schemastatement.Predicate == subPropertyOf && schemastatement.Object is Entity)
-				AddRelation(schemastatement.Subject, (Entity)schemastatement.Object, superprops, subprops);
-			if (schemastatement.Predicate == domain && schemastatement.Object is Entity) {
-				AddRelation(schemastatement.Subject, (Entity)schemastatement.Object, domains, domainof);
-				AddRelation((Entity)schemastatement.Object, rdfsresource, superclasses, subclasses);
-			}
-			if (schemastatement.Predicate == range && schemastatement.Object is Entity) {
-				AddRelation(schemastatement.Subject, (Entity)schemastatement.Object, ranges, rangeof);
-				AddRelation((Entity)schemastatement.Object, rdfsresource, superclasses, subclasses);
-			}
+				AddRelation(schemastatement.Subject, (Entity)schemastatement.Object, superprops, subprops, true);
+			if (schemastatement.Predicate == domain && schemastatement.Object is Entity)
+				AddRelation(schemastatement.Subject, (Entity)schemastatement.Object, domains, domainof, false);
+			if (schemastatement.Predicate == range && schemastatement.Object is Entity)
+				AddRelation(schemastatement.Subject, (Entity)schemastatement.Object, ranges, rangeof, false);
 		}
 		
-		void AddRelation(Entity a, Entity b, Hashtable supers, Hashtable subs) {
-			AddRelation(a, b, supers);
-			AddRelation(b, a, subs);
+		void AddRelation(Entity a, Entity b, Hashtable supers, Hashtable subs, bool incself) {
+			AddRelation(a, b, supers, incself);
+			AddRelation(b, a, subs, incself);
 		}
 		
-		void AddRelation(Entity a, Entity b, Hashtable h) {
+		void AddRelation(Entity a, Entity b, Hashtable h, bool incself) {
 			ResSet r = (ResSet)h[a];
 			if (r == null) {
 				r = new ResSet();
 				h[a] = r;
+				if (incself) r.Add(a);
 			}
 			r.Add(b);
 		}
@@ -100,9 +100,24 @@
 			}
 		}
 		
-		public override bool Distinct { get { return false; } }
+		public bool Distinct { get { return false; } }
+		
+		public void Select(StatementSink sink) { data.Select(sink); }
 		
-		public override void Select(SelectFilter filter, SelectableSource data, StatementSink sink) {
+		public bool Contains(Statement template) {
+			return Store.DefaultContains(this, template);
+		}
+		
+		public void Select(Statement template, StatementSink sink) {
+			if (template.Predicate == null) {
+				data.Select(template, sink);
+				return;
+			}
+			
+			Select(new SelectFilter(template), sink);
+		}
+		
+		public void Select(SelectFilter filter, StatementSink sink) {
 			if (filter.Predicates == null || filter.LiteralFilters != null) {
 				data.Select(filter, sink);
 				return;
@@ -122,32 +137,30 @@
 						// or what things have those types?
 						
 						// Expand objects by the subclass closure of the objects
-						data.Select(new SelectFilter(subjects, new Entity[] { p }, GetClosure(objects, subclasses, true), metas), sink);
+						data.Select(new SelectFilter(subjects, new Entity[] { p }, GetClosure(objects, subclasses), metas), sink);
 						
 						// Process domains and ranges.
 						ResSet dom = new ResSet(), ran = new ResSet();
 						Hashtable domPropToType = new Hashtable();
 						Hashtable ranPropToType = new Hashtable();
-						foreach (Entity e in GetClosure(objects, subclasses, true)) {
-							Entity[] dc = GetClosure((ResSet)domainof[e], subprops, true);
+						foreach (Entity e in objects) {
+							Entity[] dc = GetClosure((ResSet)domainof[e], subprops);
 							if (dc != null)
 							foreach (Entity c in dc) {
 								dom.Add(c);
-								AddRelation(c, e, domPropToType);
+								AddRelation(c, e, domPropToType, false);
 							}
 							
-							dc = GetClosure((ResSet)rangeof[e], subprops, true);
+							dc = GetClosure((ResSet)rangeof[e], subprops);
 							if (dc != null)
 							foreach (Entity c in dc) {
 								ran.Add(c);
-								AddRelation(c, e, ranPropToType);
+								AddRelation(c, e, ranPropToType, false);
 							}
 						}
 						
 						// If it's in the domain of any of these properties,
-						// we know its type.  Only do this if subjects are given,
-						// since otherwise we have to select for all of the values
-						// of all of these properties, and that doesn't scale well.
+						// we know its type.
 						if (subjects != null) {
 							if (dom.Count > 0) data.Select(new SelectFilter(subjects, dom.ToEntityArray(), null, metas), new ExpandDomRan(0, domPropToType, sink));
 							if (ran.Count > 0) data.Select(new SelectFilter(null, ran.ToEntityArray(), subjects, metas), new ExpandDomRan(1, ranPropToType, sink));
@@ -176,17 +189,17 @@
 					
 					if (subjects != null && objects != null) {
 						// Expand objects by the subs closure of the objects.
-						data.Select(new SelectFilter(subjects, new Entity[] { p }, GetClosure(objects, subs, true), metas), sink);
+						data.Select(new SelectFilter(subjects, new Entity[] { p }, GetClosure(objects, subs), metas), sink);
 					} else if (subjects != null) {
 						// get all of the supers of all of the subjects
 						foreach (Entity s in subjects)
-							foreach (Entity o in GetClosure(s, supers, false))
+							foreach (Entity o in GetClosure(new Entity[] { s }, supers))
 								sink.Add(new Statement(s, p, o));
 					} else if (objects != null) {
 						// get all of the subs of all of the objects
 						foreach (Resource o in objects) {
 							if (o is Literal) continue;
-							foreach (Entity s in GetClosure((Entity)o, subs, false))
+							foreach (Entity s in GetClosure(new Entity[] { (Entity)o }, subs))
 								sink.Add(new Statement(s, p, (Entity)o));
 						}
 					} else {
@@ -207,8 +220,8 @@
 				ResSet qprops = new ResSet();
 				Hashtable propfrom = new Hashtable();
 				foreach (Entity p in remainingPredicates) { 
-					foreach (Entity sp in GetClosure(p, subprops, true)) {
-						AddRelation(sp, p, propfrom);
+					foreach (Entity sp in GetClosure(new Entity[] { p }, subprops)) {
+						AddRelation(sp, p, propfrom, false);
 						qprops.Add(sp);
 					}
 				}
@@ -223,33 +236,27 @@
 			}
 		}
 		
-		static Entity[] GetClosure(Entity start, Hashtable table, bool includeStart) {
-			return GetClosure( new Resource[] { start } , table, includeStart);
-		}
-
-		static Entity[] GetClosure(ResSet starts, Hashtable table, bool includeStarts) {
+		static Entity[] GetClosure(ResSet starts, Hashtable table) {
 			if (starts == null) return null;
-			return GetClosure(starts.ToArray(), table, includeStarts);
+			return GetClosure(starts.ToArray(), table);
 		}
 		
-		static Entity[] GetClosure(Resource[] starts, Hashtable table, bool includeStarts) {
+		static Entity[] GetClosure(Resource[] starts, Hashtable table) {
 			ResSet ret = new ResSet();
 			ResSet toadd = new ResSet(starts);
-			bool firstRound = true;
 			while (toadd.Count > 0) {
 				ResSet newadd = new ResSet();
 				
 				foreach (Resource e in toadd) {
 					if (!(e is Entity)) continue;
 					if (ret.Contains(e)) continue;
-					if (!(firstRound && !includeStarts)) ret.Add(e);
+					ret.Add(e);
 					if (table.ContainsKey(e))
 						newadd.AddRange((ResSet)table[e]);
 				}
 				
 				toadd.Clear();
 				toadd.AddRange(newadd);
-				firstRound = false;
 			}
 			return ret.ToEntityArray();
 		}
@@ -259,7 +266,7 @@
 			StatementSink sink;
 			public Expand(Hashtable t, StatementSink s) { table = t; sink = s; }
 			public bool Add(Statement s) {
-				foreach (Entity e in RDFS.GetClosure(new Resource[] { s.Object }, table, true))
+				foreach (Entity e in RDFS.GetClosure(new Resource[] { s.Object }, table))
 					if (!sink.Add(new Statement(s.Subject, s.Predicate, e, s.Meta)))
 						return false;
 				return true;
@@ -303,7 +310,7 @@
 				if (domran == 1 && !(s.Object is Entity)) return true;
 				ResSet rs = (ResSet)table[s.Predicate];
 				if (rs == null) return true;
-				foreach (Entity e in RDFS.GetClosure(rs, superclasses, true)) {
+				foreach (Entity e in RDFS.GetClosure(rs, superclasses)) {
 					Statement s1 = new Statement(
 						domran == 0 ? s.Subject : (Entity)s.Object,
 						type,
@@ -354,109 +361,6 @@
 				}
 			}
 		}
-		
-		public override SemWeb.Query.MetaQueryResult MetaQuery(Statement[] graph, SemWeb.Query.QueryOptions options, SelectableSource data) {
-			Statement[] graph2;
-			SemWeb.Query.QueryOptions options2;
-			RewriteGraph(graph, options, out graph2, out options2, null);
-			
-			if (!(data is QueryableSource))
-				return new SimpleEntailment().MetaQuery(graph2, options2, data);
-			else
-				return ((QueryableSource)data).MetaQuery(graph2, options2);
-		}
-
-		public override void Query(Statement[] graph, SemWeb.Query.QueryOptions options, SelectableSource data, SemWeb.Query.QueryResultSink sink) {
-			Statement[] graph2;
-			SemWeb.Query.QueryOptions options2;
-			RewriteGraph(graph, options, out graph2, out options2, sink);
-			
-			// TODO: Because we add variables to the query when we replace things with closures,
-			// we should filter the query results so we don't pass back the bindings for those
-			// variables to the caller.
-		
-			if (!(data is QueryableSource))
-				new SimpleEntailment().Query(graph2, options2, data, sink);
-			else
-				((QueryableSource)data).Query(graph2, options2, sink);
-		}
-
-		void RewriteGraph(Statement[] graph, SemWeb.Query.QueryOptions options, out Statement[] graph2, out SemWeb.Query.QueryOptions options2, SemWeb.Query.QueryResultSink sink) {
-			graph2 = new Statement[graph.Length];
-			options2 = new SemWeb.Query.QueryOptions();
-			
-			options2.DistinguishedVariables = options.DistinguishedVariables;
-			options2.Limit = options.Limit;
-			options2.VariableKnownValues = (options.VariableKnownValues == null ? new VarKnownValuesType() : new VarKnownValuesType(options.VariableKnownValues));
-			options2.VariableLiteralFilters = options.VariableLiteralFilters;
-			
-			for (int i = 0; i < graph.Length; i++) {
-				graph2[i] = graph[i];
-			
-				//ResSet subj = GetQueryRes(graph[i], 0, options);
-				ResSet pred = GetQueryRes(graph[i], 1, options);
-				ResSet obj = GetQueryRes(graph[i], 2, options);
-				
-				if (pred.Count == 1 && pred.Contains(type)) {
-					// in an ?x rdf:type ___ query, replace ___ with the subclass closure of ___.
-					if (obj.Count > 0) {
-						Entity[] sc = GetClosure(obj, subclasses, true);
-						if (sc.Length != obj.Count && sink != null)
-							sink.AddComments("Expanding object of " + graph[i] + " with subclass closure to [" + ToString(sc) + "]");
-						SetQueryRes(ref graph2[i], 2, options2, sc);
-					}
-				}
-				
-				// expand properties into subproperties after the above tests,
-				// because we want to be sure the property was originally
-				// just one of the recognized properties
-
-				if (pred.Count > 0) {
-					Entity[] pc = GetClosure(pred, subprops, true);
-					SetQueryRes(ref graph2[i], 1, options2, pc);
-					if (pc.Length != pred.Count && sink != null)
-						sink.AddComments("Expanding predicate of " + graph[i] + " with subproperty closure to [" + ToString(pc) + "]");
-				}
-			}
-		}
-
-		ResSet GetQueryRes(Statement s, int i, SemWeb.Query.QueryOptions options) {
-			ResSet ret = new ResSet();
-			Resource r = s.GetComponent(i);
-			if (r == null) return ret;
-
-			if (!(r is Variable)) ret.Add(r);
-			
-			if (options.VariableKnownValues != null && r is Variable
-#if !DOTNET2
-				&& options.VariableKnownValues.Contains((Variable)r)) {
-#else
-				&& options.VariableKnownValues.ContainsKey((Variable)r)) {
-#endif
-				ret.AddRange((ResourceList)options.VariableKnownValues[(Variable)r]);
-			}
-			return ret;
-		}
-		
-		void SetQueryRes(ref Statement s, int i, SemWeb.Query.QueryOptions options, Entity[] values) {
-			// TODO: what if s had originally a variable in position i?
-			if (values.Length == 0)
-				s.SetComponent(i, null);
-			else if (values.Length == 1)
-				s.SetComponent(i, values[0]);
-			else {
-				Variable v = new Variable();
-				s.SetComponent(i, v);
-				options.VariableKnownValues[v] = values;
-			}
-		}
-		
-		string ToString(Entity[] ents) {
-			string[] names = new string[ents.Length];
-			for (int i = 0; i < ents.Length; i++)
-				names[i] = ents[i].ToString();
-			return String.Join(" , ", names);
-		}
 	}
 
 }

Modified: trunk/semweb/README
==============================================================================
--- trunk/semweb/README	(original)
+++ trunk/semweb/README	Fri May 16 18:52:24 2008
@@ -1,126 +1,16 @@
-SemWeb: A Semantic Web Library for C#/.NET
-==========================================
+Semantic Web Library for C#/.NET
+================================
 
-By Joshua Tauberer <http://razor.occams.info>
+By Joshua Tauberer <tauberer for net>
 
-http://razor.occams.info/code/semweb
+http://taubz.for.net/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 source files and binaries are all GPL-compatible.
-
-Most of the source files were written by me, some source files were 
-written by or are derived from other work, and the binaries are a mix of 
-the above. So the particular license that applies in each case may be
-different. However, everything included can be reused under the terms
-of the GPL (if not something more permissive, depending on what it is).
-
-The portions of this library not written by someone else are Copyright 
-2005-2008 Joshua Tauberer, and are dual-licensed under both the GNU GPL 
-(version 2 or later) and the Creative Commons Attribution License. All 
-source files not listed below were written originally by me. Thus for 
-those source files written by me, you have two license options.
-
-The following components of this library are derived from other works:
-
-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. 
+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.
 
-SQLServerStore.cs was contributed by Khaled Hammouda and is licensed
-under the GPL.

Added: trunk/semweb/RSquary.cs
==============================================================================
--- (empty file)
+++ trunk/semweb/RSquary.cs	Fri May 16 18:52:24 2008
@@ -0,0 +1,103 @@
+using System;
+using System.Collections;
+using System.IO;
+using System.Text;
+
+using SemWeb;
+using SemWeb.Stores;
+
+namespace SemWeb.Query {
+
+	public class SparqlXmlQuerySink : QueryResultSink {
+		System.Xml.XmlWriter output;
+		
+		int blankNodeCounter = 0;
+		Hashtable blankNodes = new Hashtable();
+		
+		private static System.Xml.XmlWriter GetWriter(System.IO.TextWriter writer) {
+			System.Xml.XmlTextWriter w = new System.Xml.XmlTextWriter(writer);
+			w.Formatting = System.Xml.Formatting.Indented;
+			return w;
+		}
+		
+		public SparqlXmlQuerySink(TextWriter output)
+		 : this(GetWriter(output)) {
+		}
+
+		public SparqlXmlQuerySink(System.Xml.XmlWriter output) {
+			this.output = output;
+		}
+		
+		public override void AddComments(string comments) {
+			if (comments != null && comments.Length > 0)
+				output.WriteComment(comments);
+		}
+		
+		public override void Init(VariableBinding[] variables, bool distinct, bool ordered) {
+			output.WriteStartElement("sparql");
+			output.WriteAttributeString("xmlns", "http://www.w3.org/2005/sparql-results#";);
+			output.WriteStartElement("head");
+			foreach (VariableBinding var in variables) {
+				if (var.Name == null) continue;
+				output.WriteStartElement("variable");
+				output.WriteAttributeString("name", var.Name);
+				output.WriteEndElement();
+			}
+			output.WriteEndElement(); // head
+			output.WriteStartElement("results");
+			output.WriteAttributeString("ordered", ordered ? "true" : "false");
+			output.WriteAttributeString("distinct", distinct ? "true" : "false");
+			
+			// instead of <results>, we might want <boolean>true</boolean>
+		}
+		
+		public override bool Add(VariableBinding[] result) {
+			output.WriteStartElement("result");
+			foreach (VariableBinding var in result) {
+				if (var.Name == null) continue;
+				
+				output.WriteStartElement("binding");
+				output.WriteAttributeString("name", var.Name);
+				if (var.Target == null) {
+					output.WriteStartElement("unbound");
+					output.WriteEndElement();
+				} else if (var.Target.Uri != null) {
+					output.WriteElementString("uri", var.Target.Uri);
+				} else if (var.Target is Literal) {
+					output.WriteStartElement("literal");
+					Literal literal = (Literal)var.Target;
+					if (literal.DataType != null)
+						output.WriteAttributeString("datatype", literal.DataType);
+					if (literal.Language != null)
+						output.WriteAttributeString("xml", "lang", null, literal.Language);
+					output.WriteString(literal.Value);
+					output.WriteEndElement();				
+				} else {
+					string id;
+					if (blankNodes.ContainsKey(var.Target))
+						id = (string)blankNodes[var.Target];
+					else {
+						id = "r" + (++blankNodeCounter);
+						blankNodes[var.Target] = id;
+					}
+					output.WriteStartElement("bnode");
+					output.WriteString(id);
+					output.WriteEndElement();
+				}
+				
+				output.WriteEndElement();
+			}
+			output.WriteEndElement();
+			
+			return true;
+		}
+		
+		public override void Finished() {
+			output.WriteEndElement(); // results
+			output.WriteEndElement(); // sparql
+			output.Flush();
+		}
+	}
+
+}	
+

Added: trunk/semweb/RSquaryFilters.cs
==============================================================================
--- (empty file)
+++ trunk/semweb/RSquaryFilters.cs	Fri May 16 18:52:24 2008
@@ -0,0 +1,177 @@
+using System;
+using System.Collections;
+
+using SemWeb;
+
+namespace SemWeb.Query {
+	public abstract class ValueFilterFactory {
+		public abstract ValueFilter GetValueFilter(string predicate, Resource obj);
+	}
+	
+	public abstract class ValueFilter {
+		public static Entity qFilterStringContains = "http://purl.oclc.org/NET/rsquary/string-contains";;
+		
+		public static Entity qFilterLT = "http://purl.oclc.org/NET/rsquary/lt";;
+		public static Entity qFilterLE = "http://purl.oclc.org/NET/rsquary/le";;
+		public static Entity qFilterNE = "http://purl.oclc.org/NET/rsquary/ne";;
+		public static Entity qFilterEQ = "http://purl.oclc.org/NET/rsquary/eq";;
+		public static Entity qFilterGT = "http://purl.oclc.org/NET/rsquary/gt";;
+		public static Entity qFilterGE = "http://purl.oclc.org/NET/rsquary/ge";;
+		
+		public abstract bool Filter(Resource resource, Store targetModel);
+
+		public static ValueFilter GetValueFilter(Entity predicate, Resource obj) {
+			if (predicate == qFilterStringContains && obj is Literal)
+				return new StringContainsFilter((Literal)obj);
+			if (obj is Literal && (predicate == qFilterLT || predicate == qFilterLE || predicate == qFilterNE || predicate == qFilterEQ || predicate == qFilterGT || predicate == qFilterGE)) {
+				Literal lit = (Literal)obj;
+				int c = 0; bool e = false;
+				if (predicate == qFilterLT || predicate == qFilterLE) c = -1;
+				if (predicate == qFilterGT || predicate == qFilterGE) c = 1;
+				if (predicate == qFilterLE || predicate == qFilterGE) e = true;
+				if (predicate == qFilterEQ) e = true;
+				
+				if (lit.DataType == null || lit.DataType == "" || lit.DataType == "http://www.w3.org/2001/XMLSchema#string"; || lit.DataType == "http://www.w3.org/2001/XMLSchema#normalizedString";)
+					return new StringCompareFilter(lit, c, e);
+				
+				if (lit.DataType == "http://www.w3.org/2001/XMLSchema#float"; || lit.DataType == "http://www.w3.org/2001/XMLSchema#double"; || lit.DataType == "http://www.w3.org/2001/XMLSchema#decimal"; || lit.DataType == "http://www.w3.org/2001/XMLSchema#integer"; || lit.DataType == "http://www.w3.org/2001/XMLSchema#nonPositiveInteger"; || lit.DataType == "http://www.w3.org/2001/XMLSchema#negativeInteger"; || lit.DataType == "http://www.w3.org/2001/XMLSchema#long"; || lit.DataType == "http://www.w3.org/2001/XMLSchema#int"; || lit.DataType == "http://www.w3.org/2001/XMLSchema#short"; || lit.DataType == "http://www.w3.org/2001/XMLSchema#byte"; || lit.DataType == "http://www.w3.org/2001/XMLSchema#nonNegativeInteger"; || lit.DataType == "http://www.w3.org/2001/XMLSchema#unsignedLong"; || lit.DataType == "http://www.w3.org/2001/XMLSchema#unsignedInt"; || lit.DataType == "http://www.w3.org/2001/XMLSchema#unsignedShort"; || lit.DataType == "http://www.w3.org/2001/XMLSchema#unsignedByte"; || lit.DataType == 
 "http://www.w3.org/2001/XMLSchema#positiveInteger";)
+					return new NumericCompareFilter(lit, c, e);
+				
+				if (lit.DataType == "http://www.w3.org/2001/XMLSchema#dateTime"; || lit.DataType == "http://www.w3.org/2001/XMLSchema#date"; || lit.DataType == "http://www.w3.org/2001/XMLSchema#time";)
+					return new DateTimeCompareFilter(lit, c, e);
+
+				if (lit.DataType == "http://www.w3.org/2001/XMLSchema#duration";)
+					return new TimeSpanCompareFilter(lit, c, e);
+			}
+			return null;
+		}
+		
+	}
+	
+	public abstract class LiteralValueFilter : ValueFilter {
+	}
+
+	internal abstract class StringFilter : LiteralValueFilter {
+		protected readonly string pattern;
+		public StringFilter(Literal res) : this(res.Value) {
+		}
+		public StringFilter(string pattern) {
+			this.pattern = pattern;
+		}
+	}
+	
+	internal class StringCompareFilter : StringFilter {
+		int compare;
+		bool eq;
+		
+		// Specify:
+		//   compareResult  orEqual  Meaning
+		//        -1         false   Less Than
+		//        -1         true    Less Than Or Equal
+		//         0         false   Not Equal
+		//         0         true    Equal
+		//         1         false   Greater Than
+		//         1         true    Greater Than Or Equal
+		
+		public StringCompareFilter(Literal res, int compareResult, bool orEqual) : base(res) { compare = compareResult; eq = orEqual; }
+		public StringCompareFilter(string pattern, int compareResult, bool orEqual) : base(pattern) { compare = compareResult; eq = orEqual; }
+		
+		public override bool Filter(Resource resource, Store targetModel) {
+			string v = ((Literal)resource).Value;
+			int c = v.CompareTo(pattern);
+			if (compare == 0) return (c == 0) ^ !eq;
+			return c == compare || (c == 0 && eq);
+		}
+	}	
+
+	internal class StringContainsFilter : StringFilter {
+		public StringContainsFilter(Literal res) : base(res) { }
+		public StringContainsFilter(string pattern) : base(pattern) { }
+		
+		public override bool Filter(Resource resource, Store targetModel) {
+			string v = ((Literal)resource).Value;
+			return v.IndexOf(pattern) != -1;
+		}
+	}
+	
+	internal abstract class NumericFilter : LiteralValueFilter {
+		protected readonly Decimal number;
+		public NumericFilter(Literal res) : this(int.Parse(res.Value)) { }
+		public NumericFilter(Decimal number) { this.number = number; }
+		
+	}
+
+	internal class NumericCompareFilter : NumericFilter {
+		int compare;
+		bool eq;
+		
+		public NumericCompareFilter(Literal res, int compareResult, bool orEqual) : base(res) { compare = compareResult; eq = orEqual; }
+		public NumericCompareFilter(Decimal number, int compareResult, bool orEqual) : base(number) { compare = compareResult; eq = orEqual; }
+		
+		public override bool Filter(Resource resource, Store targetModel) {
+			string v = ((Literal)resource).Value;
+			try {
+				Decimal i = Decimal.Parse(v);
+				int c = i.CompareTo(number);
+				if (compare == 0) return (c == 0) ^ !eq;
+				return c == compare || (c == 0 && eq);
+			} catch (Exception e) {
+				return false;
+			}
+		}
+	}
+
+	internal abstract class DateTimeFilter : LiteralValueFilter {
+		protected readonly DateTime datetime;
+		public DateTimeFilter(Literal res) : this(DateTime.Parse(res.Value)) { }
+		public DateTimeFilter(DateTime datetime) { this.datetime = datetime; }
+		
+	}
+
+	internal class DateTimeCompareFilter : DateTimeFilter {
+		int compare;
+		bool eq;
+		
+		public DateTimeCompareFilter(Literal res, int compareResult, bool orEqual) : base(res) { compare = compareResult; eq = orEqual; }
+		public DateTimeCompareFilter(DateTime datetime, int compareResult, bool orEqual) : base(datetime) { compare = compareResult; eq = orEqual; }
+		
+		public override bool Filter(Resource resource, Store targetModel) {
+			string v = ((Literal)resource).Value;
+			try {
+				DateTime i = DateTime.Parse(v);
+				int c = i.CompareTo(datetime);
+				if (compare == 0) return (c == 0) ^ !eq;
+				return c == compare || (c == 0 && eq);
+			} catch (Exception e) {
+				return false;
+			}
+		}
+	}
+	
+	internal abstract class TimeSpanFilter : LiteralValueFilter {
+		protected readonly TimeSpan timespan;
+		public TimeSpanFilter(Literal res) : this(TimeSpan.Parse(res.Value)) { }
+		public TimeSpanFilter(TimeSpan timespan) { this.timespan = timespan; }
+		
+	}
+
+	internal class TimeSpanCompareFilter : TimeSpanFilter {
+		int compare;
+		bool eq;
+		
+		public TimeSpanCompareFilter(Literal res, int compareResult, bool orEqual) : base(res) { compare = compareResult; eq = orEqual; }
+		public TimeSpanCompareFilter(TimeSpan timespan, int compareResult, bool orEqual) : base(timespan) { compare = compareResult; eq = orEqual; }
+		
+		public override bool Filter(Resource resource, Store targetModel) {
+			string v = ((Literal)resource).Value;
+			try {
+				TimeSpan i = TimeSpan.Parse(v);
+				int c = i.CompareTo(timespan);
+				if (compare == 0) return (c == 0) ^ !eq;
+				return c == compare || (c == 0 && eq);
+			} catch (Exception e) {
+				return false;
+			}
+		}
+	}	
+}

Modified: trunk/semweb/RdfReader.cs
==============================================================================
--- trunk/semweb/RdfReader.cs	(original)
+++ trunk/semweb/RdfReader.cs	Fri May 16 18:52:24 2008
@@ -1,194 +1,148 @@
-using System;
-using System.Collections;
-using System.IO;
-using System.Web;
-
-#if !DOTNET2
-using VariableSet = System.Collections.Hashtable;
-using VariableList = System.Collections.ICollection;
-using WarningsList = System.Collections.ArrayList;
-#else
-using VariableSet = System.Collections.Generic.Dictionary<SemWeb.Variable,SemWeb.Variable>;
-using VariableList = System.Collections.Generic.ICollection<SemWeb.Variable>;
-using WarningsList = System.Collections.Generic.List<string>;
-#endif
- 
-namespace SemWeb {
-	public class ParserException : ApplicationException {
-		public ParserException (string message) : base (message) {}
-		public ParserException (string message, Exception cause) : base (message, cause) {}
-	}
-
-	public abstract class RdfReader : StatementSource, IDisposable {
-		Entity meta = Statement.DefaultMeta;
-		string baseuri = null;
-		WarningsList warnings = new WarningsList();
-		VariableSet variables = new VariableSet();
-		bool reuseentities = false;
-		NamespaceManager nsmgr = new NamespaceManager();
-
-		public Entity Meta {
-			get {
-				return meta;
-			}
-			set {
-				meta = value;
-			}
-		}
-		
-		public string BaseUri {
-			get {
-				return baseuri;
-			}
-			set {
-				baseuri = value;
-			}
-		}
-		
-		public bool ReuseEntities {
-			get {
-				return reuseentities;
-			}
-			set {
-				reuseentities = value;
-			}
-		}
-		
-		bool StatementSource.Distinct { get { return false; } }
-		
-		public NamespaceManager Namespaces { get { return nsmgr; } }
-		
-		public VariableList Variables { get { return variables.Keys; } }
-		
-		#if !DOTNET2
-		public IList Warnings { get { return ArrayList.ReadOnly(warnings); } }
-		#else
-		public System.Collections.Generic.ICollection<string> Warnings { get { return warnings.AsReadOnly(); } }
-		#endif
-		
-		protected void AddVariable(Variable variable) {
-			variables[variable] = variable;
-		}
-
-		public abstract void Select(StatementSink sink);
-		
-		public virtual void Dispose() {
-		}
-		
-		internal static string NormalizeMimeType(string type) {
-			switch (type) {
-				case "text/xml":
-				case "application/xml":
-				case "application/rdf+xml":
-					return "xml";
-
-				case "text/n3":
-				case "text/rdf+n3":
-				case "application/n3":
-				case "application/turtle":
-				case "application/x-turtle":
-					return "n3";
-			}
-			
-			return type;
-		}
-		
-		public static RdfReader Create(string type, string source) {
-			type = NormalizeMimeType(type);
-		
-			switch (type) {
-				case "xml":
-					return new RdfXmlReader(source);
-				case "n3":
-					return new N3Reader(source);
-				default:
-					throw new ArgumentException("Unknown parser or MIME type: " + type);
-			}
-		}
-		
-		public static RdfReader Create(string type, Stream source) {
-			type = NormalizeMimeType(type);
-
-			switch (type) {
-				case "xml":
-					return new RdfXmlReader(source);
-				case "n3":
-					return new N3Reader(new StreamReader(source, System.Text.Encoding.UTF8));
-				default:
-					throw new ArgumentException("Unknown parser or MIME type: " + type);
-			}
-		}
-
-		public static RdfReader LoadFromUri(Uri webresource) {
-			// TODO: Add Accept header for HTTP resources.
-			
-			System.Net.WebRequest rq = System.Net.WebRequest.Create(webresource);
-			System.Net.WebResponse resp = rq.GetResponse();
-			
-			string mimetype = resp.ContentType;
-			if (mimetype.IndexOf(';') > -1)
-				mimetype = mimetype.Substring(0, mimetype.IndexOf(';'));
-				
-			mimetype = NormalizeMimeType(mimetype.Trim());
-			
-			RdfReader reader;
-			
-			if (mimetype == "xml" || mimetype == "application/rss+xml")
-				reader = new RdfXmlReader(resp.GetResponseStream());
-					
-			else if (mimetype == "n3")
-				reader = new N3Reader(new StreamReader(resp.GetResponseStream(), System.Text.Encoding.UTF8));
-			
-			else if (webresource.LocalPath.EndsWith(".rdf") || webresource.LocalPath.EndsWith(".xml") || webresource.LocalPath.EndsWith(".rss"))
-				reader = new RdfXmlReader(resp.GetResponseStream());
-			
-			else if (webresource.LocalPath.EndsWith(".n3") || webresource.LocalPath.EndsWith(".ttl") || webresource.LocalPath.EndsWith(".nt"))
-				reader = new N3Reader(new StreamReader(resp.GetResponseStream(), System.Text.Encoding.UTF8));
-
-			else
-				throw new InvalidOperationException("Could not determine the RDF format of the resource.");
-				
-			reader.BaseUri = resp.ResponseUri.ToString();
-			
-			return reader;
-		}
-		
-		internal static TextReader GetReader(string file) {
-			if (file == "-") return Console.In;
-			return new StreamReader(file);
-		}
-		
-		protected void OnWarning(string message) {
-			warnings.Add(message);
-		}
-		
-		internal string GetAbsoluteUri(string baseuri, string uri) {
+using System;
+using System.Collections;
+using System.IO;
+using System.Web;
+ 
+namespace SemWeb {
+	public class ParserException : ApplicationException {
+		public ParserException (string message) : base (message) {}
+		public ParserException (string message, Exception cause) : base (message, cause) {}
+	}
+
+	public abstract class RdfReader : StatementSource, IDisposable {
+		Entity meta = Statement.DefaultMeta;
+		string baseuri = null;
+		ArrayList warnings = new ArrayList();
+		Hashtable variables = new Hashtable();
+		bool reuseentities = false;
+		NamespaceManager nsmgr = new NamespaceManager();
+
+		public Entity Meta {
+			get {
+				return meta;
+			}
+			set {
+				meta = value;
+			}
+		}
+		
+		public string BaseUri {
+			get {
+				return baseuri;
+			}
+			set {
+				baseuri = value;
+			}
+		}
+		
+		public bool ReuseEntities {
+			get {
+				return reuseentities;
+			}
+			set {
+				reuseentities = value;
+			}
+		}
+		
+		bool StatementSource.Distinct { get { return false; } }
+		
+		public NamespaceManager Namespaces { get { return nsmgr; } }
+		
+		public ICollection Variables { get { return variables.Keys; } }
+		
+		public IList Warnings { get { return ArrayList.ReadOnly(warnings); } }
+		
+		protected void AddVariable(Variable variable) {
+			variables[variable] = variable;
+		}
+
+		public abstract void Select(StatementSink sink);
+		
+		public virtual void Dispose() {
+		}
+		
+		public static RdfReader Create(string type, string source) {
+			switch (type) {
+				case "xml":
+				case "text/xml":
+					return new RdfXmlReader(source);
+				case "n3":
+				case "text/n3":
+					return new N3Reader(source);
+				default:
+					throw new ArgumentException("Unknown parser type: " + type);
+			}
+		}
+		
+		public static RdfReader LoadFromUri(Uri webresource) {
+			// TODO: Add Accept header for HTTP resources.
+			
+			System.Net.WebRequest rq = System.Net.WebRequest.Create(webresource);
+			System.Net.WebResponse resp = rq.GetResponse();
+			
+			string mimetype = resp.ContentType;
+			if (mimetype.IndexOf(';') > -1)
+				mimetype = mimetype.Substring(0, mimetype.IndexOf(';'));
+			
+			switch (mimetype.Trim()) {
+				case "text/xml":
+				case "application/xml":
+				case "application/rss+xml":
+				case "application/rdf+xml":
+					return new RdfXmlReader(resp.GetResponseStream());
+					
+				case "text/rdf+n3":
+				case "application/n3":
+				case "application/turtle":
+				case "application/x-turtle":
+					return new N3Reader(new StreamReader(resp.GetResponseStream(), System.Text.Encoding.UTF8));
+			}
+			
+			if (webresource.LocalPath.EndsWith(".rdf") || webresource.LocalPath.EndsWith(".xml") || webresource.LocalPath.EndsWith(".rss"))
+				return new RdfXmlReader(resp.GetResponseStream());
+			
+			if (webresource.LocalPath.EndsWith(".n3") || webresource.LocalPath.EndsWith(".ttl") || webresource.LocalPath.EndsWith(".nt"))
+				return new N3Reader(new StreamReader(resp.GetResponseStream(), System.Text.Encoding.UTF8));
+
+			throw new InvalidOperationException("Could not determine the RDF format of the resource.");
+		}
+		
+		internal static TextReader GetReader(string file) {
+			if (file == "-") return Console.In;
+			return new StreamReader(file);
+		}
+		
+		protected void OnWarning(string message) {
+			warnings.Add(message);
+		}
+		
+		internal string GetAbsoluteUri(string baseuri, string uri) {
 			if (baseuri == null) {
-				if (uri == "")
-					throw new ParserException("An empty relative URI was found in the document but could not be converted into an absolute URI because no base URI is known for the document.");
+				//if (uri == "")
+				//throw new ParserException("An empty relative URI was found in the document but could not be converted into an absolute URI because no base URI is known for the document.");
 				return uri;
-			}
-			if (uri.IndexOf(':') != -1) return uri;
-			try {
-				UriBuilder b = new UriBuilder(baseuri);
-				b.Fragment = null; // per W3 RDF/XML test suite
-				return new Uri(b.Uri, uri, true).ToString();
-			} catch (UriFormatException) {
-				return baseuri + uri;
-			}			
-		}
-
-	}
-	
-	internal class MultiRdfReader : RdfReader {
-		private ArrayList parsers = new ArrayList();
-		
-		public ArrayList Parsers { get { return parsers; } }
-		
-		public override void Select(StatementSink storage) {
-			foreach (RdfReader p in Parsers)
-				p.Select(storage);
-		}
-	}
-}
-
+			}
+			if (uri.IndexOf(':') != -1) return uri;
+			try {
+				UriBuilder b = new UriBuilder(baseuri);
+				b.Fragment = null; // per W3 RDF/XML test suite
+				return new Uri(b.Uri, uri, true).ToString();
+			} catch (UriFormatException e) {
+				return baseuri + uri;
+			}			
+		}
+
+	}
+	
+	internal class MultiRdfReader : RdfReader {
+		private ArrayList parsers = new ArrayList();
+		
+		public ArrayList Parsers { get { return parsers; } }
+		
+		public override void Select(StatementSink storage) {
+			foreach (RdfReader p in Parsers)
+				p.Select(storage);
+		}
+	}
+}
+

Modified: trunk/semweb/RdfWriter.cs
==============================================================================
--- trunk/semweb/RdfWriter.cs	(original)
+++ trunk/semweb/RdfWriter.cs	Fri May 16 18:52:24 2008
@@ -20,8 +20,8 @@
 
 		protected object GetResourceKey(Resource resource) {
 			return resource.GetResourceKey(this);
-		}
-
+		}
+
 		protected void SetResourceKey(Resource resource, object value) {
 			resource.SetResourceKey(this, value);
 		}
@@ -41,7 +41,7 @@
 
 		public virtual void Close() {
 			if (closed) return;
-			closed = true;
+			closed = true;
 		}
 		
 		public virtual void Write(StatementSource source) {
@@ -51,39 +51,5 @@
 		void IDisposable.Dispose() {
 			Close();
 		}
-		
-		public static RdfWriter Create(string type, TextWriter output) {
-			switch (RdfReader.NormalizeMimeType(type)) {
-				case "xml":
-					#if !SILVERLIGHT
-						return new RdfXmlWriter(output);
-					#else
-						throw new NotSupportedException("RDF/XML output is not supported by the Silverlight build of the SemWeb library.");
-					#endif
-				case "n3":
-					return new N3Writer(output);
-				default:
-					throw new ArgumentException("Unknown parser or MIME type: " + type);
-			}
-		}
-
-		public static RdfWriter Create(string type, string file) {
-			switch (RdfReader.NormalizeMimeType(type)) {
-				case "xml":
-					#if !SILVERLIGHT
-						return new RdfXmlWriter(file);
-					#else
-						throw new NotSupportedException("RDF/XML output is not supported by the Silverlight build of the SemWeb library.");
-					#endif
-				case "n3":
-					return new N3Writer(file);
-				default:
-					throw new ArgumentException("Unknown parser or MIME type: " + type);
-			}
-		}
-	}
-	
-	public interface CanForgetBNodes {
-		void ForgetBNode(BNode bnode);
 	}
 }

Modified: trunk/semweb/RdfXmlReader.cs
==============================================================================
--- trunk/semweb/RdfXmlReader.cs	(original)
+++ trunk/semweb/RdfXmlReader.cs	Fri May 16 18:52:24 2008
@@ -28,57 +28,25 @@
 			rdfObject = "http://www.w3.org/1999/02/22-rdf-syntax-ns#object";,
 			rdfStatement = "http://www.w3.org/1999/02/22-rdf-syntax-ns#Statement";;
 		
-		#if !SILVERLIGHT
 		public RdfXmlReader(XmlDocument document) {
 			xml = new XmlBaseAwareReader(new XmlNodeReader(document));
-			LoadNamespaces();
 		}
-		#endif
 		
 		public RdfXmlReader(XmlReader document) {
-			XmlValidatingReader reader = new XmlValidatingReader(document); // decodes entity definitions
+			XmlValidatingReader reader = new XmlValidatingReader(document);
 			reader.ValidationType = ValidationType.None;
 			xml = new XmlBaseAwareReader(reader);
-			LoadNamespaces();
 		}
 		
 		public RdfXmlReader(TextReader document) : this(new XmlTextReader(document)) {
 		}
 
-		public RdfXmlReader(Stream document) : this(new StreamReader(document)) {
+		public RdfXmlReader(Stream document) : this(new XmlTextReader(document)) {
 		}
 
-		public RdfXmlReader(TextReader document, string baseUri) : this(document) {
-			BaseUri = baseUri;
-		}
-
-		public RdfXmlReader(Stream document, string baseUri) : this(new StreamReader(document), baseUri) {
-		}
-
-		public RdfXmlReader(string file, string baseUri) : this(GetReader(file), baseUri) {
-		}
-
-		public RdfXmlReader(string file) : this(GetReader(file), "file:///" + file) {
+		public RdfXmlReader(string file) : this(GetReader(file)) {
 		}
 		
-		private void LoadNamespaces() {
-			// Move to the document element and load any namespace
-			// declarations on the node.
-
-			while (xml.Read()) {
-				if (xml.NodeType != XmlNodeType.Element) continue;
-
-				if (xml.MoveToFirstAttribute()) {
-					do {
-						if (xml.Prefix == "xmlns")
-							Namespaces.AddNamespace(xml.Value, xml.LocalName);
-					} while (xml.MoveToNextAttribute());
-					xml.MoveToElement();
-				}
-				break;
-			}
-		}
-
 		public override void Select(StatementSink storage) {
 			// Read past the processing instructions to
 			// the document element.  If it is rdf:RDF,
@@ -87,14 +55,8 @@
 			// description.
 			
 			this.storage = storage;
-
-			bool first = true; // on the first iteration don't
-											   // advance to the next node -- we already did that
-			while (first || xml.Read()) {
-				first = false;
-
-				if (xml.NodeType != XmlNodeType.Element) continue;
-				
+									
+			while (xml.Read()) {
 				if (xml.NamespaceURI == NS.RDF && xml.LocalName == "RDF" ) {
 					// If there is an xml:base here, set BaseUri so
 					// the application can recover it.  It doesn't
@@ -107,12 +69,8 @@
 						if (xml.NodeType == XmlNodeType.Element)
 							ParseDescription();
 					}
-					
-				} else {
-					ParseDescription();
-				
+					break;
 				}
-				break;
 			}
 
 			xml.Close();
@@ -129,14 +87,7 @@
 			if (xml.NamespaceURI == "" && BaseUri == null)
 				return "#" + xml.LocalName;
 
-			return CheckUri(xml.NamespaceURI + xml.LocalName);
-		}
-		
-		private string CheckUri(string uri) {
-			string error = Entity.ValidateUri(uri);
-			if (error != null)
-				OnWarning("The URI <" + uri + "> is not valid: " + error);
-			return uri;
+			return xml.NamespaceURI + xml.LocalName;
 		}
 		
 		private int isset(string attribute) {
@@ -144,7 +95,7 @@
 		}
 		
 		private string Unrelativize(string uri) {
-			return CheckUri(GetAbsoluteUri(xml.BaseURI != "" ? xml.BaseURI : BaseUri, uri));
+			return GetAbsoluteUri(xml.BaseURI != "" ? xml.BaseURI : BaseUri, uri);
 		}
 		
 		private Entity GetBlankNode(string nodeID) {
@@ -176,6 +127,8 @@
 			
 			string nodeID = xml.GetAttribute("nodeID", NS.RDF);
 			string about = xml.GetAttribute("about", NS.RDF);
+			//if (about == null)
+			//	about = xml.GetAttribute("about");
 			string ID = xml.GetAttribute("ID", NS.RDF);
 			if (isset(nodeID) + isset(about) + isset(ID) > 1)
 				OnError("An entity description cannot specify more than one of rdf:nodeID, rdf:about, and rdf:ID");
@@ -202,12 +155,11 @@
 			
 			// If the name of the element is not rdf:Description,
 			// then the name gives its type.
-			string curnode = CurNode();
-			if (curnode != NS.RDF + "Description") {
-				if (IsRestrictedName(curnode) || IsDeprecatedName(curnode))
+			if (CurNode() != NS.RDF + "Description") {
+				if (IsRestrictedName(CurNode()) || IsDeprecatedName(CurNode()))
 					OnError(xml.Name + " cannot be the type of a resource.");
-				if (curnode == NS.RDF + "li") OnError("rdf:li cannot be the type of a resource");
-				storage.Add(new Statement(entity, rdfType, (Entity)curnode, Meta));
+				if (CurNode() == NS.RDF + "li") OnError("rdf:li cannot be the type of a resource");
+				storage.Add(new Statement(entity, rdfType, (Entity)CurNode(), Meta));
 			}
 			
 			ParsePropertyAttributes(entity);
@@ -351,10 +303,8 @@
 				}
 			
 			} else if (parseType != null && parseType == "Literal") {
-				if (datatype != null)
-					OnError("The attribute rdf:datatype is not valid on a predicate whose parseType is Literal.");
-
-				datatype = "http://www.w3.org/1999/02/22-rdf-syntax-ns#XMLLiteral";;
+				if (datatype == null)
+					datatype = "http://www.w3.org/1999/02/22-rdf-syntax-ns#XMLLiteral";;
 				
 				if (ParsePropertyAttributes(new BNode()))
 					OnError("Property attributes are not valid when parseType is Literal");

Modified: trunk/semweb/RdfXmlWriter.cs
==============================================================================
--- trunk/semweb/RdfXmlWriter.cs	(original)
+++ trunk/semweb/RdfXmlWriter.cs	Fri May 16 18:52:24 2008
@@ -6,43 +6,13 @@
 
 using SemWeb;
 
-// Since this class relies on the XmlDocument class,
-// it must be excluded completely from the Silverlight
-// build.
-#if !SILVERLIGHT
-
 namespace SemWeb {
 	public class RdfXmlWriter : RdfWriter {
-	
-		public class Options {
-			public bool UseTypedNodes = true;
-			public bool UseRdfID = true;
-			public bool UseRdfLI = true;
-			public bool EmbedNamedNodes = true;
-			public bool UsePredicateAttributes = true;
-			public bool UseParseTypeLiteral = true;
-			
-			internal bool UseParseTypeResource = false; // this is broken because it uses Clone(), which breaks references in Hashtables
-			
-			public static Options Full = new Options();
-			public static Options XMP;
-			
-			static Options() {
-				XMP = new Options();
-				XMP.UseTypedNodes = false;
-				XMP.UseRdfID = false;
-				XMP.UseParseTypeLiteral = false;
-				XMP.UsePredicateAttributes = false;
-			}
-		}
-				
-		Options opts;
 		XmlWriter writer;
 		NamespaceManager ns = new NamespaceManager();
 		
 		XmlDocument doc;
 		bool initialized = false;
-		bool closeStream = false;
 		
 		Hashtable nodeMap = new Hashtable();
 		
@@ -51,37 +21,14 @@
 		Hashtable nameAlloc = new Hashtable();
 		Hashtable nodeReferences = new Hashtable();
 		ArrayList predicateNodes = new ArrayList();
-		Hashtable nodeLiCounter = new Hashtable();
 		
 		static Entity rdftype = "http://www.w3.org/1999/02/22-rdf-syntax-ns#type";;
-		static Entity rdfli = "http://www.w3.org/1999/02/22-rdf-syntax-ns#li";;
-		static string RDFNS_ = NS.RDF + "_";
-		
-		public RdfXmlWriter(XmlDocument dest) : this(dest, Options.Full) { }
-		
-		public RdfXmlWriter(string file) : this (file, Options.Full) { }
-
-		public RdfXmlWriter(TextWriter writer) : this(writer, Options.Full) { }
-		
-		public RdfXmlWriter(XmlWriter writer) : this(writer, Options.Full) { }
 		
-		public RdfXmlWriter(XmlDocument dest, Options style) {
-			if (dest == null) throw new ArgumentNullException("dest");
-			if (style == null) throw new ArgumentNullException("style");
-			doc = dest;
-			opts = style;
-		}
+		public RdfXmlWriter(XmlDocument dest) { doc = dest; }
 		
-		public RdfXmlWriter(string file, Options style) : this(GetWriter(file), style) { closeStream = true; }
+		public RdfXmlWriter(string file) : this(GetWriter(file)) { }
 
-		public RdfXmlWriter(TextWriter writer, Options style) : this(NewWriter(writer), style) { }
-		
-		public RdfXmlWriter(XmlWriter writer, Options style) {
-			if (writer == null) throw new ArgumentNullException("writer");
-			if (style == null) throw new ArgumentNullException("style");
-			this.writer = writer;
-			this.opts = style;
-		}
+		public RdfXmlWriter(TextWriter writer) : this(NewWriter(writer)) { }
 		
 		private static XmlWriter NewWriter(TextWriter writer) {
 			XmlTextWriter ret = new XmlTextWriter(writer);
@@ -92,6 +39,10 @@
 			return ret;
 		}
 		
+		public RdfXmlWriter(XmlWriter writer) {
+			this.writer = writer;
+		}
+		
 		private void Start() {
 			if (initialized) return;
 			initialized = true;
@@ -99,6 +50,7 @@
 			if (doc == null) doc = new XmlDocument();
 			
 			doc.AppendChild(doc.CreateXmlDeclaration("1.0", null, null));
+			
 			string rdfprefix = ns.GetPrefix(NS.RDF);
 			if (rdfprefix == null) {
 				if (ns.GetNamespace("rdf") == null) {
@@ -152,13 +104,16 @@
 			
 			// TODO: Make sure the local name (here and anywhere in this
 			// class) is a valid XML name.
-			
 			if (Namespaces.GetPrefix(n) != null) {
 				prefix = Namespaces.GetPrefix(n);
 				return;
 			}
 			
 			prefix = uri.Substring(prev+1, last-prev-1);
+
+			if (prefix == "xmlns")
+				prefix = "";
+
 			
 			// Remove all non-xmlable (letter) characters.
 			StringBuilder newprefix = new StringBuilder();
@@ -167,8 +122,8 @@
 					newprefix.Append(c);
 			prefix = newprefix.ToString();
 			
-			if (prefix.Length == 0 || prefix == "xmlns") {
-				// There were no letters in the prefix or the prefix was "xmlns", which isn't valid!
+			if (prefix.Length == 0) {
+				// There were no letters in the prefix!
 				prefix = "ns";
 			}
 			
@@ -206,34 +161,17 @@
 				// Check if we have to add new type information to the existing node.
 				if (ret.NamespaceURI + ret.LocalName == NS.RDF + "Description") {
 					// Replace the untyped node with a typed node, copying in
-					// all of the attributes and children of the old node.
+					// all of the children of the old node.
 					string prefix, localname;
 					Normalize(type, out prefix, out localname);
 					XmlElement newnode = doc.CreateElement(prefix + ":" + localname, ns.GetNamespace(prefix));
 					
-					ArrayList children = new ArrayList();
-					foreach (XmlNode childnode in ret)
-						children.Add(childnode);
-					foreach (XmlNode childnode in children) {
-						ret.RemoveChild(childnode);
-						newnode.AppendChild(childnode);
+					foreach (XmlNode childnode in ret) {
+						newnode.AppendChild(childnode.Clone());
 					}
-						
-					foreach (XmlAttribute childattr in ret.Attributes)
-						newnode.Attributes.Append((XmlAttribute)childattr.Clone());
-						
-					ret.ParentNode.ReplaceChild(newnode, ret);
 					
+					ret.ParentNode.ReplaceChild(newnode, ret);
 					nodeMap[entity] = newnode;
-					if (nodeReferences.ContainsKey(ret)) {
-						nodeReferences[newnode] = nodeReferences[ret];
-						nodeReferences.Remove(ret);
-					}
-					if (nodeLiCounter.ContainsKey(ret)) {
-						nodeLiCounter[newnode] = nodeLiCounter[ret];
-						nodeLiCounter.Remove(ret);
-					}
-					
 					return newnode;
 				} else {
 					// The node is already typed, so just add a type predicate.
@@ -260,8 +198,6 @@
 					SetAttribute(node, NS.RDF, ns.GetPrefix(NS.RDF), "about", uri);
 				else if (fragment.Length == 0)
 					SetAttribute(node, NS.RDF, ns.GetPrefix(NS.RDF), "about", "");
-				else if (!opts.UseRdfID)
-					SetAttribute(node, NS.RDF, ns.GetPrefix(NS.RDF), "about", uri);
 				else
 					SetAttribute(node, NS.RDF, ns.GetPrefix(NS.RDF), "ID", fragment.Substring(1)); // chop off hash
 			} else {
@@ -280,19 +216,7 @@
 		
 		private XmlElement CreatePredicate(XmlElement subject, Entity predicate) {
 			if (predicate.Uri == null)
-				throw new InvalidOperationException("Predicates cannot be blank nodes when serializing RDF to XML.");
-			
-			if (opts.UseRdfLI && predicate.Uri.StartsWith(RDFNS_)) {
-				try {
-					int n = int.Parse(predicate.Uri.Substring(RDFNS_.Length));
-					int expected = nodeLiCounter.ContainsKey(subject) ? (int)nodeLiCounter[subject] : 1;
-					if (n == expected) {
-						predicate = rdfli;
-						nodeLiCounter[subject] = expected+1;
-					}
-				} catch {
-				}
-			}			
+				throw new InvalidOperationException("Predicates cannot be blank nodes.");
 			
 			string prefix, localname;
 			Normalize(predicate.Uri, out prefix, out localname);
@@ -307,27 +231,14 @@
 		
 			XmlElement subjnode;
 			
-			bool hastype = opts.UseTypedNodes && statement.Predicate == rdftype && statement.Object.Uri != null;
+			bool hastype = statement.Predicate == rdftype && statement.Object.Uri != null;
 			subjnode = GetNode(statement.Subject, hastype ? statement.Object.Uri : null, null);
 			if (hastype) return;
 
 			XmlElement prednode = CreatePredicate(subjnode, statement.Predicate);
 			
 			if (!(statement.Object is Literal)) {
-				if (!nodeMap.ContainsKey(statement.Object) && (opts.EmbedNamedNodes || statement.Object.Uri == null)) {
-					// Embed the object node right within the predicate node
-					// if we haven't already created a node for the object
-					// and if we're allowed to do so.
-					GetNode((Entity)statement.Object, null, prednode);
-
-				} else {
-					// Otherwise, we will reference the object with a
-					// rdf:resource or rdf:nodeID attribute.
-					
-					// Create the object node at top-level if a node doesn't exist.
-					if (!nodeMap.ContainsKey(statement.Object))
-						GetNode((Entity)statement.Object, null, null);
-
+				if (nodeMap.ContainsKey(statement.Object)) {
 					if (statement.Object.Uri != null) {
 						string uri = statement.Object.Uri, fragment;
 						if (Relativize(statement.Object.Uri, out fragment))
@@ -346,10 +257,12 @@
 						nodeReferences[nodeMap[statement.Object]] = null;
 					else
 						nodeReferences[nodeMap[statement.Object]] = prednode;
+				} else {
+					GetNode((Entity)statement.Object, null, prednode);
 				}
 			} else {
 				Literal literal = (Literal)statement.Object;
-				if (opts.UseParseTypeLiteral && literal.DataType != null && literal.DataType == "http://www.w3.org/1999/02/22-rdf-syntax-ns#XMLLiteral";) {
+				if (literal.DataType != null && literal.DataType == "http://www.w3.org/1999/02/22-rdf-syntax-ns#XMLLiteral";) {
 					prednode.InnerXml = literal.Value;
 					SetAttribute(prednode, NS.RDF, ns.GetPrefix(NS.RDF), "parseType", "Literal");
 				} else {
@@ -377,38 +290,18 @@
 			}
 		}
 		
-		void MakeDocumentNice() {
+		public override void Close() {
+			Start(); // make sure the document node was written
+
 			// For any node that was referenced by exactly one predicate,
 			// move the node into that predicate, provided the subject
 			// isn't itself!
 			foreach (DictionaryEntry e in nodeReferences) {
+				if (e.Value == null) continue; // referenced by more than one predicate
 				XmlElement node = (XmlElement)e.Key;
 				XmlElement predicate = (XmlElement)e.Value;
-				
-				// Node is already embedded somewhere.
-				if (node.ParentNode != node.OwnerDocument.DocumentElement)
-					continue;
-				
-				// Node is referenced by more than one predicate
-				if (predicate == null) continue;
-
-				// The option to do this for named nodes is turned off.
-				if (!opts.EmbedNamedNodes && node.HasAttribute("about", NS.RDF))
-					continue;
-				
-				// we can have circular references between nodes (also
-				// between a node and itself),
-				// which we can't nicely collapse this way.  Make sure
-				// that the predicate we want to insert ourselves into
-				// is not a descendant of the node we're moving!
-				XmlNode ancestry = predicate.ParentNode;
-				bool canMove = true;
-				while (ancestry != null) {
-					if (ancestry == node) { canMove = false; break; }
-					ancestry = ancestry.ParentNode;
-				}
-				if (!canMove) continue;
-
+				if (node.ParentNode != node.OwnerDocument.DocumentElement) continue; // already referenced somewhere
+				if (predicate.ParentNode == node) continue; // can't insert node as child of itself
 				node.ParentNode.RemoveChild(node);
 				predicate.AppendChild(node);
 				predicate.RemoveAttribute("resource", NS.RDF); // it's on the lower node
@@ -435,19 +328,15 @@
 				if (obj.Attributes.Count == 1 && obj.Attributes[0].NamespaceURI+obj.Attributes[0].LocalName != NS.RDF+"about") continue;
 				
 				// See if all its predicates are literal with no attributes.
-				bool hasSimpleLits = false;
 				bool allSimpleLits = true;
 				foreach (XmlElement opred in obj.ChildNodes) {
 					if (opred.FirstChild is XmlElement)
 						allSimpleLits = false;
 					if (opred.Attributes.Count > 0)
 						allSimpleLits = false;
-					hasSimpleLits = true;
 				}
 						
-				if (hasSimpleLits && allSimpleLits && obj.ChildNodes.Count <= 3) {
-					if (!opts.UsePredicateAttributes) continue;
-				
+				if (allSimpleLits) {
 					// Condense by moving all of obj's elements to attributes of the predicate,
 					// and turning a rdf:about into a rdf:resource, and then remove obj completely.
 					if (obj.Attributes.Count == 1)
@@ -458,18 +347,7 @@
 					
 					if (pred.ChildNodes.Count == 0) pred.IsEmpty = true;
 
-				} else if (obj.ChildNodes.Count == 0 && obj.Attributes.Count == 1) {
-				
-					// Condense by turning a rdf:about into a rdf:resource,
-					// and then remove obj completely.
-					SetAttribute(pred, NS.RDF, ns.GetPrefix(NS.RDF), "resource", obj.Attributes[0].Value);
-					pred.RemoveChild(obj);
-					
-					if (pred.ChildNodes.Count == 0) pred.IsEmpty = true;
-					
 				} else if (obj.Attributes.Count == 0) { // no rdf:about
-					if (!opts.UseParseTypeResource) continue;
-
 					// Condense this node using parseType=Resource
 					pred.RemoveChild(obj);
 					foreach (XmlElement opred in obj.ChildNodes)
@@ -477,21 +355,12 @@
 					SetAttribute(pred, NS.RDF, ns.GetPrefix(NS.RDF), "parseType", "Resource");
 				}
 			}
-		}
-		
-		public override void Close() {
-			Start(); // make sure the document node was written
-			
-			MakeDocumentNice();
 
 			base.Close();
 			
 			if (writer != null) {
 				doc.WriteTo(writer);
-				if (closeStream)
-					writer.Close();
-				else
-					writer.Flush();
+				//writer.Close();
 			}
 		}
 		
@@ -506,6 +375,5 @@
 		}
 		
 	}
-}
 
-#endif
+}

Added: trunk/semweb/Remote.cs
==============================================================================
--- (empty file)
+++ trunk/semweb/Remote.cs	Fri May 16 18:52:24 2008
@@ -0,0 +1,271 @@
+using System;
+using System.Collections;
+using System.IO;
+using System.Text;
+using System.Web;
+using System.Xml;
+ 
+namespace SemWeb.Remote {
+
+	public class SparqlHttpSource : SelectableSource {
+	
+		string url;
+		
+		public SparqlHttpSource(string url) {
+			this.url = url;
+		}
+
+		public bool Distinct { get { return true; } }
+		
+		public bool Contains(Statement template) {
+			return Select(template, null, true);
+		}
+		
+		public void Select(StatementSink sink) {
+			Select(Statement.All, sink);
+		}
+		
+		public void Select(Statement template, StatementSink sink) {
+			Select(template, sink, false);
+		}
+		
+		bool Select(Statement template, StatementSink sink, bool ask) {
+			return Select(
+				template.Subject == null ? null : new Entity[] { template.Subject },
+				template.Predicate == null ? null : new Entity[] { template.Predicate },
+				template.Object == null ? null : new Resource[] { template.Object },
+				template.Meta == null ? null : new Entity[] { template.Meta },
+				null,
+				0,
+				sink,
+				ask
+				);
+		}
+		
+		public void Select(SelectFilter filter, StatementSink sink) {
+			Select(filter.Subjects, filter.Predicates, filter.Objects, filter.Metas, filter.LiteralFilters, filter.Limit, sink, false);
+		}
+		
+		bool Select(Entity[] subjects, Entity[] predicates, Resource[] objects, Entity[] metas, LiteralFilter[] litFilters, int limit, StatementSink sink, bool ask) {
+			// TODO: Change meta into named graphs.  Anything but a null or DefaultMeta
+			// meta returns no statements immediately.
+			if (metas != null && (metas.Length != 1 || metas[0] != Statement.DefaultMeta))
+				return false;
+		
+			string query;
+			bool nonull = false;
+		
+			if (subjects != null && subjects.Length == 1
+				&& predicates != null && predicates.Length == 1
+				&& objects != null && objects.Length == 1) {
+				query = "ASK WHERE { " + S(subjects[0], null) + " " + S(predicates[0], null) + " " + S(objects[0], null) + "}";
+				nonull = true;
+			} else {
+				if (ask)
+					query = "ASK";
+				else
+					query = "SELECT *";
+				query += " WHERE { ";
+				query += S(subjects, "subject");
+				query += " ";
+				query += S(predicates, "predicate");
+				query += " ";
+				query += S(objects, "object");
+				query += " . ";
+				query += SL(subjects, "subject");
+				query += SL(predicates, "predicate");
+				query += SL(objects, "object");
+				query += " }";
+				
+				// TODO: Pass literal filters to server.
+			}
+			
+			if (limit >= 1)
+				query += " LIMIT " + limit;
+			
+			XmlDocument result = Load(query);
+			
+			if (ask || nonull) {
+				foreach (XmlElement boolean in result.DocumentElement) {
+					if (boolean.Name != "boolean") continue;
+					bool ret = boolean.InnerText == "true";
+					if (ask)
+						return ret;
+					else if (ret)
+						sink.Add(new Statement(subjects[0], predicates[0], objects[0]));
+					return false;
+				}
+				throw new ApplicationException("Invalid server response: No boolean node.");
+			}
+			
+			XmlElement bindings = null;
+			foreach (XmlElement e in result.DocumentElement)
+				if (e.Name == "results")
+					bindings = e;
+			if (bindings == null)
+				throw new ApplicationException("Invalid server response: No result node.");
+			
+			MemoryStore distinctCheck = null;
+			if (bindings.GetAttribute("distinct") != "true")
+				distinctCheck = new MemoryStore();
+			
+			Hashtable bnodes = new Hashtable();
+			
+			foreach (XmlNode bindingnode in bindings) {
+				if (!(bindingnode is XmlElement)) continue;
+				XmlElement binding = (XmlElement)bindingnode;
+				Resource subj = GetBinding(binding, "subject", subjects, bnodes);
+				Resource pred = GetBinding(binding, "predicate", predicates, bnodes);
+				Resource obj = GetBinding(binding, "object", objects, bnodes);
+				if (!(subj is Entity) || !(pred is Entity)) continue;
+				Statement s = new Statement((Entity)subj, (Entity)pred, obj);
+				if (distinctCheck != null && distinctCheck.Contains(s)) continue;
+				if (litFilters != null && !LiteralFilter.MatchesFilters(s.Object, litFilters, this)) continue;
+				if (!sink.Add(s)) return true;
+				if (distinctCheck != null) distinctCheck.Add(s);
+			}
+			
+			return true;
+		}
+		
+		string S(Resource[] r, string v) {
+			if (r == null || r.Length != 1) return "?" + v;
+			return S(r[0], null);
+		}
+		string SL(Resource[] r, string v) {
+			if (r == null || r.Length <= 1) return "";
+			StringBuilder ret = new StringBuilder();
+			ret.Append("FILTER(");
+			bool first = true;
+			for (int i = 0; i < r.Length; i++) {
+				if (r[i].Uri == null) continue;
+				if (!first) ret.Append(" || "); first = false;
+				ret.Append('?');
+				ret.Append(v);
+				ret.Append("=<");
+				if (r[i].Uri != null)
+					ret.Append(r[i].Uri);
+				ret.Append('>');
+			}
+			ret.Append(").");
+			if (first) return "";
+			return ret.ToString();
+		}
+		
+		string S(Resource r, string v) {
+			if (r == null) {
+				return v;
+			} else if (r is Literal) {
+				return r.ToString();
+			} else if (r.Uri != null) {
+				if (r.Uri.IndexOf('>') != -1)
+					throw new ArgumentException("Invalid URI: " + r.Uri);
+				return "<" + r.Uri + ">";
+			} else {
+				throw new ArgumentException("Blank node in select not supported.");
+			}
+		}
+		
+		Resource GetBinding(XmlElement binding, string v, Resource[] values, Hashtable bnodes) {
+			if (values != null && values.Length == 1) return values[0];
+			
+			XmlElement b = (XmlElement)binding.FirstChild;
+			while (b != null && b.GetAttribute("name") != v)
+				b = (XmlElement)b.NextSibling;
+			if (b == null)
+				throw new ApplicationException("Invalid server response: Not all bindings present (" + v + "): " + binding.OuterXml);
+			
+			b = (XmlElement)b.FirstChild;
+			if (b.Name == "uri")
+				return new Entity(b.InnerText);
+			else if (b.Name == "literal")
+				return new Literal(b.InnerText); // datatype/lang
+			else if (b.Name == "bnode") {
+				string id = b.InnerText;
+				if (bnodes.ContainsKey(id)) return (Entity)bnodes[id];
+				Entity ret = new BNode();
+				bnodes[id] = ret;
+				return ret;
+			}
+			throw new ApplicationException("Invalid server response: " + b.OuterXml);
+		}
+		
+		XmlDocument Load(string query) {
+			string qstr = "query=" + System.Web.HttpUtility.UrlEncode(query);
+			
+			string method = "POST";
+			
+			System.Net.WebRequest rq;
+			
+			if (method == "GET") {
+				string qurl = url + "?" + qstr;
+				rq = System.Net.WebRequest.Create(qurl);
+			} else {
+				ASCIIEncoding encoding = new ASCIIEncoding(); // ?
+				byte[] data = encoding.GetBytes(qstr);
+
+				rq = System.Net.WebRequest.Create(url);
+				rq.Method = "POST";
+				rq.ContentType="application/x-www-form-urlencoded";
+				rq.ContentLength = data.Length;
+				
+				using (Stream stream = rq.GetRequestStream())
+					stream.Write(data, 0, data.Length);
+			}
+			
+			System.Net.WebResponse resp = rq.GetResponse();
+			
+			string mimetype = resp.ContentType;
+			if (mimetype.IndexOf(';') > -1)
+				mimetype = mimetype.Substring(0, mimetype.IndexOf(';'));
+			
+			if (mimetype != "application/sparql-results+xml")
+				throw new ApplicationException("The result of the query was not a SPARQL Results document.");
+
+			XmlDocument ret = new XmlDocument();
+			ret.Load(new StreamReader(resp.GetResponseStream(), System.Text.Encoding.UTF8));
+						
+			if (ret.DocumentElement.Name != "sparql")
+				throw new ApplicationException("Invalid server response: Not a sparql results document.");
+			
+			return ret;
+		}
+		
+		public Entity[] FindEntities(Statement[] graph) {
+			string query = "SELECT ?entity WHERE { ";
+			
+			foreach (Statement s in graph) {
+				query += S(s.Subject, "?entity");
+				query += " ";
+				query += S(s.Predicate, "?entity");
+				query += " ";
+				query += S(s.Object, "?entity");
+				query += " . ";
+				if (s.Meta != Statement.DefaultMeta) return new Entity[0];
+			}
+			
+			query += "}";
+			
+			XmlDocument result = Load(query);
+			
+			XmlElement bindings = null;
+			foreach (XmlElement e in result.DocumentElement)
+				if (e.Name == "results")
+					bindings = e;
+			if (bindings == null)
+				throw new ApplicationException("Invalid server response: No result node.");
+			
+			Hashtable bnodes = new Hashtable();
+			ArrayList ret = new ArrayList();
+			
+			foreach (XmlElement binding in bindings) {
+				Entity e = (Entity)GetBinding(binding, "entity", null, bnodes);
+				ret.Add(e);
+			}
+			
+			return (Entity[])ret.ToArray(typeof(Entity));
+		}
+
+	}
+}
+

Modified: trunk/semweb/Resource.cs
==============================================================================
--- trunk/semweb/Resource.cs	(original)
+++ trunk/semweb/Resource.cs	Fri May 16 18:52:24 2008
@@ -1,14 +1,9 @@
 using System;
 using System.Collections;
-using System.Xml;
 
 namespace SemWeb {
 	
-	public abstract class Resource : IComparable
-#if DOTNET2
-	, IComparable<Resource>
-#endif
-	{
+	public abstract class Resource : IComparable {
 		internal object ekKey, ekValue;
 		internal ArrayList extraKeys;
 		
@@ -23,7 +18,7 @@
 		internal Resource() {
 		}
 		
-		// These gets rid of the warning about overring ==, !=.
+		// These get rid of the warning about overring ==, !=.
 		// Since Entity and Literal override these, we're ok.
 		public override bool Equals(object other) {
 			return base.Equals(other);
@@ -31,7 +26,7 @@
 		public override int GetHashCode() {
 			return base.GetHashCode();
 		}
-
+		
 		public static bool operator ==(Resource a, Resource b) {
 			if ((object)a == null && (object)b == null) return true;
 			if ((object)a == null || (object)b == null) return false;
@@ -41,7 +36,7 @@
 			return !(a == b);
 		}
 		
-		public object GetResourceKey(object key) {
+		internal object GetResourceKey(object key) {
 			if (ekKey == key) return ekValue;
 			if (extraKeys == null) return null;
 			for (int i = 0; i < extraKeys.Count; i++) {
@@ -51,7 +46,7 @@
 			}
 			return null;
 		}
-		public void SetResourceKey(object key, object value) {
+		internal void SetResourceKey(object key, object value) {
 			if (ekKey == null || ekKey == key) {
 				ekKey = key;
 				ekValue = value;
@@ -69,14 +64,7 @@
 			extraKeys.Add(k);
 		}
 
-#if DOTNET2
 		int IComparable.CompareTo(object other) {
-			return CompareTo((Resource)other);
-		}
-		public int CompareTo(Resource other) {
-#else
-		public int CompareTo(object other) {
-#endif
 			// We'll make an ordering over resources.
 			// First named entities, then bnodes, then literals.
 			// Named entities are sorted by URI.
@@ -91,16 +79,7 @@
 			
 			if (Uri != null) return String.Compare(Uri, r.Uri, false, System.Globalization.CultureInfo.InvariantCulture);
 			
-			if (this is BNode) {
-				BNode me = (BNode)this, o = (BNode)other;
-				if (me.LocalName != null || o.LocalName != null) {
-					if (me.LocalName == null) return -1;
-					if (o.LocalName == null) return -1;
-					int x = String.Compare(me.LocalName, o.LocalName, false, System.Globalization.CultureInfo.InvariantCulture);
-					if (x != 0) return x;
-				}
-				return GetHashCode().CompareTo(r.GetHashCode());
-			}
+			if (this is BNode) return GetHashCode().CompareTo(r.GetHashCode());
 
 			if (this is Literal) {
 				int x = String.Compare(((Literal)this).Value, ((Literal)r).Value, false, System.Globalization.CultureInfo.InvariantCulture);
@@ -120,7 +99,7 @@
 		
 		public Entity(string uri) {
 			if (uri == null) throw new ArgumentNullException("To construct entities with no URI, use the BNode class.");
-			if (uri.Length == 0) throw new ArgumentException("uri cannot be the empty string");
+			//if (uri.Length == 0) throw new ArgumentException("uri cannot be the empty string");
 			this.uri = uri;
 		}
 		
@@ -163,203 +142,6 @@
 		public override string ToString() {
 			return "<" + Uri + ">";
 		}
-		
-		public static string ValidateUri(string uri) {
-			// Validates the uri as an RDF IRI Reference, 
-			// i.e. an absolute URI with optional fragment,
-			// based on the RDF Concepts document and RFC 3987
-			// (and RFCs recursively referenced therein).
-			
-			// From the IRI RFC 3987, we are accepting:
-			//
-			//   scheme ":" ihier-part [ "?" iquery ] [ "#" ifragment ]
-			//
-			//   scheme         = ALPHA *( ALPHA | DIGIT | "+" | "-" | "." )
-			//   ihier-part     = "//" iauthority ipath-abempty | ipath-absolute | ipath-rootless | ipath-empty
-			//   iauthority     = [ iuserinfo "@" ] ihost [ ":" port ]
-			//   iuserinfo      = *( iunreserved / pct-encoded / sub-delims / ":" )
-			//   ihost          = IP-literal / IPv4address / ireg-name
-			//   port           = *DIGIT
-			//   IP-literal     = "[" ( IPv6address / IPvFuture  ) "]"
-			//   IPv4address    = dec-octet "." dec-octet "." dec-octet "." dec-octet
-			//   ipath-absolute = "/" [ isegment-nz *( "/" isegment ) ]
-			//   ipath-rootless = isegment-nz *( "/" isegment )
-			//   isegment       = *ipchar
-			//   isegment-nz    = 1*ipchar
-			//   ipath-empty    = (nothing)
-			//   iquery         = *( ipchar | iprivate | "/" | "?" )
-			//   ifragment      = *( ipchar | "/" | "?" )
-			//   ipchar         = iunreserved | pct-encoded | sub-delims | ":" | "@"
-			//   iunreserved    = ALPHA | DIGIT | "-" | "." | "_" | "~" | ucschar
-			//   pct-encoded    = "%" HEXDIG HEXDIG
-			//   sub-delims     = "!" | "$" | "&" | "'" | "(" | ")" | "*" | "+" | "," | ";" | "="
-			//   iprivate       = %xE000-F8FF | %xF0000-FFFFD | %x100000-10FFFD
-			//   ALPHA          =  %x41-5A | %x61-7A   ; A-Z / a-z
-			//   DIGIT          =  %x30-39 ; 0-9
-			//   HEXDIG         =  DIGIT / "A" / "B" / "C" / "D" / "E" / "F"
-			
-			char state = 's';
-			
-			foreach (char c in uri) {
-				// From 'RDF Concepts' section 6.4,
-				// a URI cannot contain control characters (#x00-#x1F, #x7F-#x9F)
-				if (c <= 0x1F || (c >= 0x7F && c <= 0x9F))
-					return "The control character '" + c + "' (" + ((int)c).ToString("x") + ") is not allowed.";
-			
-				switch (state) {
-
-				// scheme =  ALPHA *( ALPHA | DIGIT | "+" | "-" | "." )
-				// The scheme is terminated by a colon after the first character.
-					
-				case 's': // first character in scheme
-					if (!ValidateUriIsAlpha(c))
-						return "The character '" + c + "' (" + ((int)c).ToString("x") + ") is not allowed as the first character in a URI, which is the start of the scheme.";
-					state = 'S';
-					break;
-				
-				case 'S': // non-first character in scheme
-					if (c == ':') // transition to ihier-part
-						state = 'H';
-					else if (!ValidateUriIsAlpha(c) && !ValidateUriIsDigit(c) && c != '+' && c != '-' && c != '.')
-						return "The character '" + c + "' (" + ((int)c).ToString("x") + ") is not allowed in the scheme portion of the URI.";
-					break;
-					
-				//   ihier-part     = "//" iauthority ipath-abempty | ipath-absolute | ipath-rootless | ipath-empty
-				case 'H': // start of ihier-part (just read the colon)
-					if (c == '/')        // either start of //+iauthority+ipath-abempty or ipath-absolute 
-						state = '/';
-					else if (c == '?')   // empty ihier-part, start of query
-						state = 'q';
-					else if (c == '#')   // empty ihier-part, start of fragment
-						state = 'f';
-					else {
-						// This is the first character of ipath-rootless, which must be an ipchar
-						if (!ValidateUriIsIpchar(c))
-							return "The character '" + c + "' (" + ((int)c).ToString("x") + ") is not allowed at the start of the (rootless) path portion of the URI.";
-						state = 'r';
-					}
-					break;
-				
-				case '/': // either 2nd slash of //+iauthority+ipath-abempty or 1st character past slash in ipath-absolute
-					if (c == '/')
-						state = 'a'; // iauthority, to lead into ipath-abempty
-					else if (!ValidateUriIsIpchar(c))
-						return "The character '" + c + "' (" + ((int)c).ToString("x") + ") is not allowed at the start of the (absolute without authority) path portion of the URI.";
-					// For the rest of ipath-absolute, we go to state r.
-					state = 'r';
-					break;
-					
-				case 'r': // 2nd character and later of ipath-rootless, or
-				          // 3rd character and later of ipath-absolute, or
-				          // 2nd character and later of ipath-abempty,
-				          // all of which are *( "/" isegment ) and terminate the ihier-part of the URI.
-					if (c == '?')   // start of query
-						state = 'q';
-					else if (c == '#')   // start of fragment
-						state = 'f';
-					else if (c != '/' && !ValidateUriIsIpchar(c))
-						return "The character '" + c + "' (" + ((int)c).ToString("x") + ") is not allowed in the path portion of the URI.";
-					// stay in this state
-					break;
-
-				case 'a': // the start of iauthority, which then goes to ipath-abempty, which then terminates the ihier-part
-					// We very loosely check this part because we can't do this easily deterministically.
-					// (For instance, we don't know if we are looking at a username or host until we
-					// find an @ sign or the end.) So we allow any of the allowed characters in
-					// this region, until we can be sure we are moving into ipath-abempty with a
-					// slash, or into the query with a question mark, or fragment with a hash. 
-					// None of those three characters can occur in this part (fortunately).
-					
-					if (c == '?')   // start of query
-						state = 'q';
-					else if (c == '#')   // start of fragment
-						state = 'f';
-					else if (c == '/')   // start of a non-empty ipath-abempty, which is *( "/" isegment )
-						state = 'r';
-						
-					// The allowed characters are:
-					// iauthority: '@' | ':'
-					// iuserinfo: iunreserved / pct-encoded / sub-delims / ':'
-					// ihost: 
-					// port: DIGIT
-					// IP-literal: '[' ']'
-					// IPv6address: HEXDIG ':'
-					// IPvFuture: 'v' HEXDIG '.' unreserved subdelims ':'
-					// IPv4address: DIGIT '.'
-					// ireg-name: iunreserved / pct-encoded / sub-delims
-						
-					else if (c != '@' && c != ':' && c != '[' && c != ']' && c != '.' && c != ':' && c != '%'
-						&& !ValidateUriIsIUnreserved(c) && !ValidateUriIsSubdelim(c))
-						return "The character '" + c + "' (" + ((int)c).ToString("x") + ") is not allowed in the authority (user, host, and port) portion of the URI.";
-
-					// stay in this state
-
-					break;
-				
-				case 'q': // start of query string
-					// iquery         = *( ipchar / iprivate / "/" / "?" )
-					if (c == '#')   // start of fragment
-						state = 'f';
-					else if (c != '/' && c != '?' && !ValidateUriIsIpchar(c) && !ValidateUriIsIPrivate(c))
-						return "The character '" + c + "' (" + ((int)c).ToString("x") + ") is not allowed in the query string portion of the URI.";
-					// stay in this state
-					break;
-					
-				case 'f': // start of fragment
-					// ifragment      = *( ipchar / "/" / "?" )
-					if (c != '/' && c != '?' && !ValidateUriIsIpchar(c))
-						return "The character '" + c + "' (" + ((int)c).ToString("x") + ") is not allowed in the fragment portion of the URI.";
-					// stay in this state
-					break;
-				}
-			}
-			
-			// Which state did we end up in? If we end in some states, we didn't finish the URI.
-			switch (state) {
-			case 's': // first character in scheme: the URI was empty
-				return "The URI is empty.";
-			case 'S': // non-first character in scheme
-				return "The URI must start with a scheme name (e.g. \"http:\").";
-			case 'H': // start of ihier-part
-				return "After the scheme (e.g. \"http:\"), something must follow, such as double-slashes.";
-			case '/': // just read first slash of "//" or the starting slash in a path
-			case 'r': // various
-				// no problem: we can end here
-				break;
-			case 'a': // just read second slash starting the authority part
-				return "After the double-slashes, a host name (or a user-plus- -sign) must follow.";
-			case 'q': // start of query 
-			case 'f': // start of fragment
-				// no problem: we can have empty query strings and fragments
-				break;
-			}
-			
-			// This is an OK IRI.
-			return null;
-		}
-		
-		private static bool ValidateUriIsAlpha(char c) {
-			return (c >= 0x41 && c <= 0x5A) || (c >= 0x61 && c <= 0x7A);
-		}
-		private static bool ValidateUriIsDigit(char c) {
-			return c >= 0x30 && c <= 0x39;
-		}
-		internal static bool ValidateUriIsIUnreserved(char c) {
-			return ValidateUriIsAlpha(c) || ValidateUriIsDigit(c) || c == '-' || c == '.' || c == '_' || c == '~'
-				|| (c >= 0xA0 && c <= 0xD7FF) || (c >= 0xF900 && c <= 0xFDCF) || (c >= 0xFDF0 && c <= 0xFFEF); // ucschar
-		}
-		private static bool ValidateUriIsSubdelim(char c) {
-			return c == '!' || c == '$' || c == '&' || c == '\'' || c == '(' || c == ')' || c == '*' || c == '+' || c ==  ',' || c == ';' || c == '=';
-		}
-		private static bool ValidateUriIsIpchar(char c) {
-			// also could be pct-encoded char, but we don't have look-ahead so we just
-			// check the percent -- the rest will be OK because the HEXDIG chars could appear alone
-			return ValidateUriIsIUnreserved(c) || ValidateUriIsSubdelim(c) || c == ':' || c == '@' || c == '%';
-		}
-		private static bool ValidateUriIsIPrivate(char c) {
-			return c >= 0xE000 && c <= 0xF8FF;
-		}
-		
 	}
 	
 	public class BNode : Entity {
@@ -401,7 +183,7 @@
 			if (LocalName != null)
 				return "_:" + LocalName;
 			else
-				return "_:bnode" + Math.Abs(GetHashCode());
+				return "_:bnode" + GetHashCode();
 		}
 	}
 	
@@ -416,11 +198,13 @@
 			if (LocalName != null)
 				return "?" + LocalName;
 			else
-				return "?var" + Math.Abs(GetHashCode());
+				return "?var" + GetHashCode();
 		}
 	}
 
 	public sealed class Literal : Resource { 
+		private const string XMLSCHEMANS = "http://www.w3.org/2001/XMLSchema#";;
+
 		private string value, lang, type;
 		
 		public Literal(string value) : this(value, null, null) {
@@ -429,7 +213,7 @@
 		public Literal(string value, string language, string dataType) {
 		  if (value == null)
 			  throw new ArgumentNullException("value");
-		  this.value = value;
+		  this.value = string.Intern(value);
 		  this.lang = language;
 		  this.type = dataType;
 		  
@@ -521,28 +305,24 @@
 		
 		public object ParseValue() {
 			string dt = DataType;
-			if (dt == null || !dt.StartsWith(NS.XMLSCHEMA)) return Value;
-			dt = dt.Substring(NS.XMLSCHEMA.Length);
+			if (dt == null || !dt.StartsWith(XMLSCHEMANS)) return Value;
+			dt = dt.Substring(XMLSCHEMANS.Length);
 			
 			if (dt == "string" || dt == "normalizedString" || dt == "anyURI") return Value;
-			if (dt == "boolean") return XmlConvert.ToBoolean(Value);
-			if (dt == "decimal" || dt == "integer" || dt == "nonPositiveInteger" || dt == "negativeInteger" || dt == "nonNegativeInteger" || dt == "positiveInteger") return XmlConvert.ToDecimal(Value);
-			if (dt == "float") return XmlConvert.ToSingle(Value);
-			if (dt == "double") return XmlConvert.ToDouble(Value);
-			if (dt == "duration") return XmlConvert.ToTimeSpan(Value);
-			#if !DOTNET2
-			if (dt == "dateTime" || dt == "time" || dt == "date") return XmlConvert.ToDateTime(Value);
-			#else
-			if (dt == "dateTime" || dt == "time" || dt == "date") return XmlConvert.ToDateTime(Value, XmlDateTimeSerializationMode.Utc);
-			#endif
-			if (dt == "long") return XmlConvert.ToInt64(Value);
-			if (dt == "int") return XmlConvert.ToInt32(Value);
-			if (dt == "short") return XmlConvert.ToInt16(Value);
-			if (dt == "byte") return XmlConvert.ToSByte(Value);
-			if (dt == "unsignedLong") return XmlConvert.ToUInt64(Value);
-			if (dt == "unsignedInt") return XmlConvert.ToUInt32(Value);
-			if (dt == "unsignedShort") return XmlConvert.ToUInt16(Value);
-			if (dt == "unsignedByte") return XmlConvert.ToByte(Value);
+			if (dt == "boolean") return (Value == "true" || Value == "1");
+			if (dt == "decimal" || dt == "integer" || dt == "nonPositiveInteger" || dt == "negativeInteger" || dt == "nonNegativeInteger" || dt == "positiveInteger") return Decimal.Parse(Value);
+			if (dt == "float") return float.Parse(Value);
+			if (dt == "double") return double.Parse(Value);
+			if (dt == "duration") return TimeSpan.Parse(Value); // syntax?
+			if (dt == "dateTime" || dt == "time" || dt == "date") return DateTime.Parse(Value); // syntax?
+			if (dt == "long") return long.Parse(Value);
+			if (dt == "int") return int.Parse(Value);
+			if (dt == "short") return short.Parse(Value);
+			if (dt == "byte") return sbyte.Parse(Value);
+			if (dt == "unsignedLong") return ulong.Parse(Value);
+			if (dt == "unsignedInt") return uint.Parse(Value);
+			if (dt == "unsignedShort") return ushort.Parse(Value);
+			if (dt == "unsignedByte") return byte.Parse(Value);
 			
 			return Value;
 		}
@@ -552,82 +332,8 @@
 			return new Literal(ParseValue().ToString(), Language, DataType);
 		}
 		
-		public static Literal FromValue(float value) {
-			return new Literal(value.ToString(), null, NS.XMLSCHEMA + "float");
-		}
-		public static Literal FromValue(double value) {
-			return new Literal(value.ToString(), null, NS.XMLSCHEMA + "double");
-		}
-		public static Literal FromValue(byte value) {
-			if (value <= 127)
-				return new Literal(value.ToString(), null, NS.XMLSCHEMA + "byte");
-			else
-				return new Literal(value.ToString(), null, NS.XMLSCHEMA + "unsignedByte");
-		}
-		public static Literal FromValue(short value) {
-			return new Literal(value.ToString(), null, NS.XMLSCHEMA + "short");
-		}
-		public static Literal FromValue(int value) {
-			return new Literal(value.ToString(), null, NS.XMLSCHEMA + "int");
-		}
-		public static Literal FromValue(long value) {
-			return new Literal(value.ToString(), null, NS.XMLSCHEMA + "long");
-		}
-		public static Literal FromValue(sbyte value) {
-			return new Literal(value.ToString(), null, NS.XMLSCHEMA + "byte");
-		}
-		public static Literal FromValue(ushort value) {
-			return new Literal(value.ToString(), null, NS.XMLSCHEMA + "unsignedShort");
-		}
-		public static Literal FromValue(uint value) {
-			return new Literal(value.ToString(), null, NS.XMLSCHEMA + "unsignedInt");
-		}
-		public static Literal FromValue(ulong value) {
-			return new Literal(value.ToString(), null, NS.XMLSCHEMA + "unsignedLong");
-		}
-		public static Literal FromValue(Decimal value) {
-			return new Literal(value.ToString(), null, NS.XMLSCHEMA + "decimal");
-		}
-		public static Literal FromValue(bool value) {
-			return new Literal(value ? "true" : "false", null, NS.XMLSCHEMA + "boolean");
-		}
-		public static Literal FromValue(string value) {
-			return new Literal(value, null, NS.XMLSCHEMA + "string");
-		}
-		public static Literal FromValue(Uri value) {
-			return new Literal(value.ToString(), null, NS.XMLSCHEMA + "anyURI");
-		}
-		public static Literal FromValue(DateTime value) {
-			return FromValue(value, true, false);
-		}
-		public static Literal FromValue(DateTime value, bool withTime, bool isLocalTime) {
-			if (withTime && isLocalTime)
-				return new Literal(value.ToString("yyyy-MM-ddTHH\\:mm\\:ss.FFFFFFF0zzz"), null, NS.XMLSCHEMA + "dateTime");
-			else if (withTime)
-				return new Literal(value.ToString("yyyy-MM-ddTHH\\:mm\\:ss.FFFFFFF0"), null, NS.XMLSCHEMA + "dateTime");
-			else
-				return new Literal(value.ToString("yyyy-MM-dd"), null, NS.XMLSCHEMA + "date");
-		}
-		public static Literal FromValue(TimeSpan value) {
-			return FromValue(value, false, false);
-		}
-		public static Literal FromValue(TimeSpan value, bool asTime, bool isLocalTime) {
-			if (!asTime) {
-				string ret = (value.Ticks >= 0 ? "P" : "-P");
-				if (value.Days != 0) {
-					ret += value.Days + "D";
-					if (value.Hours != 0 || value.Minutes != 0 || value.Seconds != 0 || value.Milliseconds != 0)
-						ret += "T";
-				}
-				if (value.Hours != 0) ret += value.Hours + "H";
-				if (value.Minutes != 0) ret += value.Minutes + "M";
-				if (value.Seconds != 0 || value.Milliseconds != 0) ret += (value.Seconds + value.Milliseconds/1000) + "S";
-				return new Literal(ret, null, NS.XMLSCHEMA + "duration");
-			} else if (isLocalTime) {
-				return new Literal((DateTime.Today + value).ToString("HH\\:mm\\:ss.FFFFFFF0zzz"), null, NS.XMLSCHEMA + "time");
-			} else {
-				return new Literal((DateTime.Today + value).ToString("HH\\:mm\\:ss.FFFFFFF0"), null, NS.XMLSCHEMA + "time");
-			}
+		public static Literal Create(bool value) {
+			return new Literal(value ? "true" : "false", null, XMLSCHEMANS + "boolean");
 		}
 	}
 

Modified: trunk/semweb/SQLStore.cs
==============================================================================
--- trunk/semweb/SQLStore.cs	(original)
+++ trunk/semweb/SQLStore.cs	Fri May 16 18:52:24 2008
@@ -1,49 +1,3 @@
-/**
- * SQLStore.cs: An abstract implementation of an RDF triple store
- * using an SQL-based backend.  This class is extended by the
- * MySQLStore, SQLiteStore, and PostreSQLStore classes.
- *
- * The SQLStore creates three tables to store its data.  The tables
- * are organizes follows:
- *   table                columns
- * PREFIX_entites    id (int), value (case-sensitive string)
- * PREFIX_literals   id (int), value (case-sens. string), language (short case-insens. string),
-                     datatype (case-sense. string), hash (28byte case-sense string)
- * PREFIX_statements subject (int), predicate (int), object (int), meta (int), objecttype (tiny int)
- *
- * Every resource (named node, bnode, and literal) is given a numeric ID.
- * Zero is reserved.  One is used for the bnode in the static field Statement.DefaultMeta.
- * The numbers for entities and literals are drawn from the same set of numbers,
- * so there cannot be an entity and a literal with the same ID.
- *
- * The subject, predicate, object, and meta columns in the _statements table
- * refer to the numeric IDs of resources.  objecttype is zero if the object
- * is an entity (named or blank), otherwise one if it is a literal.  Some databases
- * (i.e. MySQL) add a UNIQUE constraint over the subject, predicate, object,
- * and meta columns so that the database is guaranteed not to have duplicate
- * rows.  Not all databases will do this, though.
- *
- * All literals have a row in the _literals table.  The value, language, and
- * datatype columns have the obvious things.  Notably, the datatype column
- * is a string column, even though you might think of it as an entity that
- * should be in the entities table.  The hash column contains a SHA1 hash
- * over the three fields and is used to speed up look-ups for literals.  It
- * also is used for creating a UNIQUE index over the table.  Because literal
- * values can be arbitrarily long, creating a fixed-size hash is the only
- * way to reliably create a UNIQUE constraint over the table.  A literal
- * value is entered into this table at most once.  You can't have two rows
- * that have the exact same value, language, and datatype.
- *
- * The _entities table contains all *named* entities.  Basically it is just
- * a mapping between entity IDs in the _statements table and their URIs.
- * Importantly, bnodes are not represented in this table (it would be a
- * waste of space since they have nothing to map IDs to).  A UNIQUE constraint
- * is placed over the value column to ensure that a URI ends up in the table
- * at most once.  Because bnodes are not in this table, the only way to
- * get a list of them is to see what IDs are used in _statements that are
- * not in this table or in the _literals table.
- */
-
 using System;
 using System.Collections;
 using System.Collections.Specialized;
@@ -55,93 +9,47 @@
 using SemWeb.Util;
 
 namespace SemWeb.Stores {
-	public abstract class SQLStore : QueryableSource, StaticSource, ModifiableSource, IDisposable {
-		// Table initialization, etc.
-		// --------------------------
+	// TODO: It's not safe to have two concurrent accesses to the same database
+	// because the creation of new entities will use the same IDs.
 	
-		// This is a version number representing the current 'schema' implemented
-		// by this class in case of future updates.
+	public abstract class SQLStore : Store, SupportsPersistableBNodes {
 		int dbformat = 1;
 	
-		// 'table' is the prefix of the tables used by this store, i.e.
-		// {table}_statements, {table}_literals, {table}_entities.  This is
-		// set in the constructor.
 		string table;
-		
-		// 'guid' is a GUID assigned to this store.  It is created the
-		// first time the SQL table structure is made and is saved in
-		// the info block of the literal with ID zero.
 		string guid;
 		
-		// this flag tracks the first access to the backend, when it
-		// creates tables and indexes if necessary
 		bool firstUse = true;
-
-		// Importing
-		// ------------
 		
-		// The SQL store operates in two modes, isImporting == false and
-		// isImporting == true.  The first mode is the usual mode, where
-		// calls to Add are executed immediately.  The second mode, which
-		// is activated by the Import() method, batches Add calls to make
-		// insertions faster.  While importing, no public methods should be
-		// called except by this class itself.
 		bool isImporting = false;
-		
-		// Each time we need to add a resource, we need to find it an ID.
-		// When importing, we track the next ID available in this field
-		// and increment it as necessary.  When not importing, we do a
-		// DB query to find the next available ID.
 		int cachedNextId = -1;
-		
-		// These variables hold on to the IDs of literals and entities during
-		// importing. They are periodically cleared.
 		Hashtable entityCache = new Hashtable();
 		Hashtable literalCache = new Hashtable();
 		
-		// This is a buffer of statements waiting to be processed.
-		StatementList addStatementBuffer = null;
-		
-		// These track the performance of our buffer so we can adjust its size
-		// on the fly to maximize performance.
-		int importAddBufferSize = 200, importAddBufferRotation = 0;
-		TimeSpan importAddBufferTime = TimeSpan.MinValue;
-		
-		// Other Flags
-		// -----------
-		
-		// When adding a statement that has a bnode in it (not while importing),
-		// we have to do a two-staged procedure.  This holds on to a list of
-		// GUIDs that we've temporarily assigned to bnodes that are cleared
-		// at the end of Add().
 		ArrayList anonEntityHeldIds = new ArrayList();
 		
-		// Tracks whether any statements have been removed from the store by this
-		// object.  When Close() is called, if true, the entities and literals
-		// tables are cleaned up to remove unreferenced resoures.
 		bool statementsRemoved = false;
 
-		// 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
-		// these all the time.
 		StringBuilder cmdBuffer = new StringBuilder();
-		 
-		// The quote character that surrounds strings in SQL statements.
-		// Initialized in the constructor.
+		
+		// Buffer statements to process together.
+		StatementList addStatementBuffer = null;
+		
+		string 	INSERT_INTO_LITERALS_VALUES,
+				INSERT_INTO_STATEMENTS_VALUES,
+				INSERT_INTO_ENTITIES_VALUES;
 		char quote;
 		
-		// Ensure that calls to Select() and Query() are synchronized to make these methods thread-safe.
 		object syncroot = new object();
 		
-		// Our SHA1 object which we use to create hashes of literal values.
+		Hashtable metaEntities;
+		
 		SHA1 sha = SHA1.Create();
 		
-		// This class is placed inside entities to cache their numeric IDs.
+		int importAddBufferSize = 200, importAddBufferRotation = 0;
+		TimeSpan importAddBufferTime = TimeSpan.MinValue;
+				
 		private class ResourceKey {
 			public int ResId;
 			
@@ -151,80 +59,41 @@
 			public override bool Equals(object other) { return (other is ResourceKey) && ((ResourceKey)other).ResId == ResId; }
 		}
 		
-		// Some helpers.
-		
-		const string rdfs_member = NS.RDFS + "member";
-		const string rdf_li = NS.RDF + "_";
-		
 		private static readonly string[] fourcols = new string[] { "subject", "predicate", "object", "meta" };
 		private static readonly string[] predcol = new string[] { "predicate" };
 		private static readonly string[] metacol = new string[] { "meta" };
 
-		private string INSERT_INTO_LITERALS_VALUES { get { return "INSERT INTO " + table + "_literals VALUES "; } }
-		private string INSERT_INTO_ENTITIES_VALUES { get { return "INSERT INTO " + table + "_entities VALUES "; } }
-		private string INSERT_INTO_STATEMENTS_VALUES { get { return "INSERT " + (HasUniqueStatementsConstraint ? InsertIgnoreCommand : "") + " INTO " + table + "_statements VALUES "; } }
-			
-
-		// The constructor called by subclasses.
 		protected SQLStore(string table) {
 			this.table = table;
 			
+			INSERT_INTO_LITERALS_VALUES = "INSERT INTO " + table + "_literals VALUES ";
+			INSERT_INTO_ENTITIES_VALUES = "INSERT INTO " + table + "_entities VALUES ";
+			INSERT_INTO_STATEMENTS_VALUES = "INSERT " + (SupportsInsertIgnore ? "IGNORE " : "") + "INTO " + table + "_statements VALUES ";
+			
 			quote = GetQuoteChar();
 		}
 		
 		protected string TableName { get { return table; } }
 		
-		// The next few abstract and virtual methods allow implementors to control
-		// what features the SQLStore takes advantage of and controls the SQL
-		// language use.
-
-		// See the API docs for more on these.
-		protected abstract bool HasUniqueStatementsConstraint { get; } // may not return true unless INSERT (IGNORE COMMAND) is supported
-		protected abstract string InsertIgnoreCommand { get; }
+		protected abstract bool SupportsNoDuplicates { get; }
+		protected abstract bool SupportsInsertIgnore { get; }
 		protected abstract bool SupportsInsertCombined { get; }
+		protected virtual bool SupportsFastJoin { get { return true; } }
 		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; } }
 		
 		protected abstract void CreateNullTest(string column, System.Text.StringBuilder command);
-		protected abstract void CreateLikeTest(string column, string prefix, int method, System.Text.StringBuilder command);
-			// method: 0 == startswith, 1 == contains, 2 == ends with
 		
-		protected virtual bool CreateEntityPrefixTest(string column, string prefix, System.Text.StringBuilder command) {
-			command.Append('(');
-			command.Append(column);
-			command.Append(" IN (SELECT id from ");
-			command.Append(TableName);
-			command.Append("_entities WHERE ");
-			CreateLikeTest("value", prefix, 0, command);
-			command.Append("))");
-			return true;
-		}
-		
-		// If this is the first use, initialize the table and index structures.
-		// CreateTable() will create tables if they don't already exist.
-		// CreateIndexes() will only be run if this is a new database, so that
-		// the user may customize the indexes after the table is first created
-		// without SemWeb adding its own indexes the next time again.
 		private void Init() {
 			if (!firstUse) return;
 			firstUse = false;
 			
 			CreateTable();
-			if (CreateVersion()) // tests if this is a new table
-				CreateIndexes();
-
-			if (InitCommands != null)
-				RunCommand(InitCommands);
+			CreateIndexes();
+			CreateVersion();
 		}
 		
-		// Creates the info block in the literal row with ID zero.  Returns true
-		// if it created a new info block (i.e. this is a new database).
-		private bool CreateVersion() {	
+		private void CreateVersion() {	
 			string verdatastr = RunScalarString("SELECT value FROM " + table + "_literals WHERE id = 0");
-			bool isNew = (verdatastr == null);
-			
 			NameValueCollection verdata = ParseVersionInfo(verdatastr);
 			
 			if (verdatastr != null && verdata["ver"] == null)
@@ -243,9 +112,7 @@
 			if (verdatastr == null)
 				RunCommand("INSERT INTO " + table + "_literals (id, value) VALUES (0, " + Escape(newverdata, true) + ")");
 			else if (verdatastr != newverdata)
-				RunCommand("UPDATE " + table + "_literals SET value = " + Escape(newverdata, true) + " WHERE id = 0");
-				
-			return isNew;
+				RunCommand("UPDATE " + table + "_literals SET value = " + newverdata + " WHERE id = 0");
 		}
 		
 		NameValueCollection ParseVersionInfo(string verdata) {
@@ -265,44 +132,27 @@
 			return ret;
 		}
 		
-		// Now we get to the Store implementation.
+		public override bool Distinct { get { return true; } }
 		
-		// Why do we return true here?
-		public bool Distinct { get { return true; } }
-		
-		public int StatementCount {
-			get {
-				Init();
-				RunAddBuffer();
-				return RunScalarInt("select count(subject) from " + table + "_statements", 0);
-			}
-		}
+		public override int StatementCount { get { Init(); RunAddBuffer(); return RunScalarInt("select count(subject) from " + table + "_statements", 0); } }
 		
 		public string GetStoreGuid() { return guid; }
 		
-		public string GetPersistentBNodeId(BNode node) {
+		public string GetNodeId(BNode node) {
 			ResourceKey rk = (ResourceKey)GetResourceKey(node);
 			if (rk == null) return null;
-			return GetStoreGuid() + ":" + rk.ResId.ToString();
+			return rk.ResId.ToString();
 		}
 		
-		public BNode GetBNodeFromPersistentId(string persistentId) {
+		public BNode GetNodeFromId(string persistentId) {
 			try {
-				int colon = persistentId.IndexOf(':');
-				if (colon == -1) return null;
-				if (GetStoreGuid() != persistentId.Substring(0, colon)) return null;
-				int id = int.Parse(persistentId.Substring(colon+1));
+				int id = int.Parse(persistentId);
 				return (BNode)MakeEntity(id, null, null);
-			} catch (Exception) {
+			} catch (Exception e) {
 				return null;
 			}
 		}
 		
-		// Returns the next ID available for a resource.  If we're importing,
-		// use and increment the cached ID.  Otherwise, scan the tables for
-		// the highest ID in use and use that plus one.  (We have to scan all
-		// tables because bnode IDs are only in the statements table and
-		// entities and literals may be orphaned so those are only in those tables.)
 		private int NextId() {
 			if (isImporting && cachedNextId != -1)
 				return ++cachedNextId;
@@ -330,36 +180,30 @@
 			if (maxid >= nextid) nextid = maxid + 1;
 		}
 		
-		// Implements Store.Clear() by dropping the tables entirely.
-		public void Clear() {
+		public override void Clear() {
 			// Drop the tables, if they exist.
-			try { RunCommand("DROP TABLE " + table + "_statements;"); } catch (Exception) { }
-			try { RunCommand("DROP TABLE " + table + "_literals;"); } catch (Exception) { }
-			try { RunCommand("DROP TABLE " + table + "_entities;"); } catch (Exception) { }
+			try { RunCommand("DROP TABLE " + table + "_statements;"); } catch (Exception e) { }
+			try { RunCommand("DROP TABLE " + table + "_literals;"); } catch (Exception e) { }
+			try { RunCommand("DROP TABLE " + table + "_entities;"); } catch (Exception e) { }
 			firstUse = true;
 		
 			Init();
 			if (addStatementBuffer != null) addStatementBuffer.Clear();
 			
+			metaEntities = null;
+
 			//RunCommand("DELETE FROM " + table + "_statements;");
 			//RunCommand("DELETE FROM " + table + "_literals;");
 			//RunCommand("DELETE FROM " + table + "_entities;");
 		}
 		
-		// Computes a hash for a literal value to put in the hash column of the _literals table.
 		private string GetLiteralHash(Literal literal) {
 			byte[] data = System.Text.Encoding.Unicode.GetBytes(literal.ToString());
 			byte[] hash = sha.ComputeHash(data);
 			return Convert.ToBase64String(hash);
 		}
 		
-		// Gets the ID of a literal in the database given an actual Literal object.
-		// If create is false, return 0 if no such literal exists in the database.
-		// Otherwise, create a row for the literal if none exists putting the
-		// SQL insertion statement into the buffer argument.
-		// If we're in isImporting mode, we expect that the literal's ID has already
-		// been pre-fetched and put into literalCache (if the literal exists in the DB).
-		private int GetLiteralId(Literal literal, bool create, StringBuilder buffer, bool insertCombined, ref bool firstInsert) {
+		private int GetLiteralId(Literal literal, bool create, StringBuilder buffer, bool insertCombined) {
 			// Returns the literal ID associated with the literal.  If a literal
 			// doesn't exist and create is true, a new literal is created,
 			// otherwise 0 is returned.
@@ -369,25 +213,20 @@
 				if (ret != null) return (int)ret;
 			} else {
 				StringBuilder b = cmdBuffer; cmdBuffer.Length = 0;
-				b.Append("SELECT ");
-				if (!SupportsLimitClause)
-					b.Append("TOP 1 ");
-				b.Append("id FROM ");
+				b.Append("SELECT id FROM ");
 				b.Append(table);
 				b.Append("_literals WHERE hash =");
-				b.Append(quote);
+				b.Append("\"");
 				b.Append(GetLiteralHash(literal));
-				b.Append(quote);
-				if (SupportsLimitClause)
-					b.Append(" LIMIT 1");
-				b.Append(';');
+				b.Append("\"");
+				b.Append(" LIMIT 1;");
 				
 				object id = RunScalar(b.ToString());
 				if (id != null) return AsInt(id);
 			}
 				
 			if (create) {
-				int id = AddLiteral(literal, buffer, insertCombined, ref firstInsert);
+				int id = AddLiteral(literal, buffer, insertCombined);
 				if (isImporting)
 					literalCache[literal] = id;
 				return id;
@@ -396,8 +235,7 @@
 			return 0;
 		}
 		
-		// Creates the SQL command to add a literal to the _literals table.
-		private int AddLiteral(Literal literal, StringBuilder buffer, bool insertCombined, ref bool firstInsert) {
+		private int AddLiteral(Literal literal, StringBuilder buffer, bool insertCombined) {
 			int id = NextId();
 			
 			StringBuilder b;
@@ -410,9 +248,8 @@
 			if (!insertCombined) {
 				b.Append(INSERT_INTO_LITERALS_VALUES);
 			} else {
-				if (!firstInsert)
+				if (b.Length > 0)
 					b.Append(',');
-				firstInsert = false;
 			}
 			b.Append('(');
 			b.Append(id);
@@ -428,11 +265,9 @@
 				EscapedAppend(b, literal.DataType);
 			else
 				b.Append("NULL");
-			b.Append(',');
-			b.Append(quote);
+			b.Append(",\"");
 			b.Append(GetLiteralHash(literal));
-			b.Append(quote);
-			b.Append(')');
+			b.Append("\")");
 			if (!insertCombined)
 				b.Append(';');
 			
@@ -444,13 +279,7 @@
 			return id;
 		}
 
-		// Gets the ID of an entity in the database given a URI.
-		// If create is false, return 0 if no such entity exists in the database.
-		// Otherwise, create a row for the entity if none exists putting the
-		// SQL insertion statement into the entityInsertBuffer argument.
-		// If we're in isImporting mode, we expect that the entity's ID has already
-		// been pre-fetched and put into entityCache (if the entity exists in the DB).
-		private int GetEntityId(string uri, bool create, StringBuilder entityInsertBuffer, bool insertCombined, bool checkIfExists, ref bool firstInsert) {
+		private int GetEntityId(string uri, bool create, StringBuilder entityInsertBuffer, bool insertCombined, bool checkIfExists) {
 			// Returns the resource ID associated with the URI.  If a resource
 			// doesn't exist and create is true, a new resource is created,
 			// otherwise 0 is returned.
@@ -473,8 +302,8 @@
 			
 			// If we got here, no such resource exists and create is true.
 			
-			if (MaximumUriLength != -1 && uri.Length > MaximumUriLength)
-				throw new NotSupportedException("URI exceeds maximum length supported by data store.");
+			if (uri.Length > 255)
+				throw new NotSupportedException("URIs must be a maximum of 255 characters for this store due to indexing constraints (before MySQL 4.1.2).");
 
 			id = NextId();
 			
@@ -488,9 +317,8 @@
 			if (!insertCombined) {
 				b.Append(INSERT_INTO_ENTITIES_VALUES);
 			} else {
-				if (!firstInsert)
+				if (b.Length > 0)
 					b.Append(',');
-				firstInsert = false;
 			}
 			b.Append('(');
 			b.Append(id);
@@ -511,30 +339,16 @@
 			return id;
 		}
 		
-		// Gets the ID of an entity in the database given a URI.
-		// If create is false, return 0 if no such entity exists in the database.
-		// Otherwise, create a row for the entity if none exists.
 		private int GetResourceId(Resource resource, bool create) {
-			bool firstLiteralInsert = true, firstEntityInsert = true;
-			return GetResourceIdBuffer(resource, create, null, null, false, ref firstLiteralInsert, ref firstEntityInsert);
+			return GetResourceIdBuffer(resource, create, null, null, false);
 		}
 		
-		// Gets the ID of an entity or literal in the database given a Resource object.
-		// If create is false, return 0 if no such entity exists in the database.
-		// Otherwise, create a row for the resource if none exists putting the
-		// SQL insertion statements into the literalInsertBuffer and entityInsertBuffer arguments.
-		// If we're in isImporting mode, we expect that the resources's ID has already
-		// been pre-fetched and put into one of the cache variables (if the resource exists in the DB).
-		// If we're trying to get the ID for a bnode (i.e. we want to add it to the database),
-		//   if we're isImporting, we know the next ID we can use -- increment and return that.
-		//   otherwise we have to create a temporary row in the _entities table to hold onto
-		//   the ID just until we add the statement, at which point the row can be removed.
-		private int GetResourceIdBuffer(Resource resource, bool create, StringBuilder literalInsertBuffer, StringBuilder entityInsertBuffer, bool insertCombined, ref bool firstLiteralInsert, ref bool firstEntityInsert) {
+		private int GetResourceIdBuffer(Resource resource, bool create, StringBuilder literalInsertBuffer, StringBuilder entityInsertBuffer, bool insertCombined) {
 			if (resource == null) return 0;
 			
 			if (resource is Literal) {
 				Literal lit = (Literal)resource;
-				return GetLiteralId(lit, create, literalInsertBuffer, insertCombined, ref firstLiteralInsert);
+				return GetLiteralId(lit, create, literalInsertBuffer, insertCombined);
 			}
 			
 			if (object.ReferenceEquals(resource, Statement.DefaultMeta))
@@ -546,7 +360,7 @@
 			int id;
 			
 			if (resource.Uri != null) {
-				id = GetEntityId(resource.Uri, create, entityInsertBuffer, insertCombined, true, ref firstEntityInsert);
+				id = GetEntityId(resource.Uri, create, entityInsertBuffer, insertCombined, true);
 			} else {
 				// This anonymous node didn't come from the database
 				// since it didn't have a resource key.  If !create,
@@ -567,7 +381,7 @@
 					// removed.
 					string guid = "semweb-bnode-guid://taubz.for.net,2006/"
 						+ Guid.NewGuid().ToString("N");
-					id = GetEntityId(guid, create, entityInsertBuffer, insertCombined, false, ref firstEntityInsert);
+					id = GetEntityId(guid, create, entityInsertBuffer, insertCombined, false);
 					anonEntityHeldIds.Add(id);
 				}
 			}
@@ -577,14 +391,11 @@
 			return id;
 		}
 
-		// Gets the type of the Resource, 0 for entities; 1 for literals.
 		private int ObjectType(Resource r) {
 			if (r is Literal) return 1;
 			return 0;
 		}
 		
-		// Creates an entity given its ID and its URI, and put it into
-		// the cache argument if the argument is not null.
 		private Entity MakeEntity(int resourceId, string uri, Hashtable cache) {
 			if (resourceId == 0)
 				return null;
@@ -611,20 +422,36 @@
 			return ent;
 		}
 		
-		// Adds a statement to the store.
-		// If we're isImoprting, buffer the statement, and if the buffer is full,
-		// run the buffer.
-		// Otherwise, add it immediately.
-		bool StatementSink.Add(Statement statement) {
-			Add(statement);
-			return true;
-		}
-		public void Add(Statement statement) {
+		public override void Add(Statement statement) {
 			if (statement.AnyNull) throw new ArgumentNullException();
 			
+			metaEntities = null;
+
 			if (addStatementBuffer != null) {
 				addStatementBuffer.Add(statement);
-				RunAddBufferDynamic();
+				
+				// This complicated code here adjusts the size of the add
+				// buffer dynamically to maximize performance.
+				int thresh = importAddBufferSize;
+				if (importAddBufferRotation == 1) thresh += 100; // experiment with changing
+				if (importAddBufferRotation == 2) thresh -= 100; // the buffer size
+				
+				if (addStatementBuffer.Count >= thresh) {
+					DateTime start = DateTime.Now;
+					RunAddBuffer();
+					TimeSpan duration = DateTime.Now - start;
+					
+					// If there was an improvement in speed, per statement, on an 
+					// experimental change in buffer size, keep the change.
+					if (importAddBufferRotation != 0
+						&& duration.TotalSeconds/thresh < importAddBufferTime.TotalSeconds/importAddBufferSize
+						&& thresh >= 200 && thresh <= 4000)
+						importAddBufferSize = thresh;
+
+					importAddBufferTime = duration;
+					importAddBufferRotation++;
+					if (importAddBufferRotation == 3) importAddBufferRotation = 0;
+				}
 				return;
 			}
 			
@@ -672,33 +499,6 @@
 			}
 		}
 		
-		private void RunAddBufferDynamic() {
-			// This complicated code here adjusts the size of the add
-			// buffer dynamically to maximize performance.
-			int thresh = importAddBufferSize;
-			if (importAddBufferRotation == 1) thresh += 100; // experiment with changing
-			if (importAddBufferRotation == 2) thresh -= 100; // the buffer size
-			
-			if (addStatementBuffer.Count >= thresh) {
-				DateTime start = DateTime.Now;
-				RunAddBuffer();
-				TimeSpan duration = DateTime.Now - start;
-				
-				if (DebugLogSpeed)
-					Console.Error.WriteLine(thresh + "\t" + thresh/duration.TotalSeconds);
-				
-				// If there was an improvement in speed, per statement, on an 
-				// experimental change in buffer size, keep the change.
-				if (importAddBufferRotation != 0
-					&& duration.TotalSeconds/thresh < importAddBufferTime.TotalSeconds/importAddBufferSize
-					&& thresh >= 200 && thresh <= 10000)
-					importAddBufferSize = thresh;
-				importAddBufferTime = duration;
-				importAddBufferRotation++;
-				if (importAddBufferRotation == 3) importAddBufferRotation = 0;
-			}
-		}
-		
 		private void RunAddBuffer() {
 			if (addStatementBuffer == null || addStatementBuffer.Count == 0) return;
 			
@@ -764,9 +564,9 @@
 					
 					if (hasLiterals)
 						cmd.Append(" , ");
-					cmd.Append(quote);
+					cmd.Append('"');
 					cmd.Append(hash);
-					cmd.Append(quote);
+					cmd.Append('"');
 					hasLiterals = true;
 					litseen[hash] = lit;
 				}
@@ -784,11 +584,6 @@
 				
 				StringBuilder entityInsertions = new StringBuilder();
 				StringBuilder literalInsertions = new StringBuilder();
-				if (insertCombined) entityInsertions.Append(INSERT_INTO_ENTITIES_VALUES);
-				if (insertCombined) literalInsertions.Append(INSERT_INTO_LITERALS_VALUES);
-				int entityInsertionsInitialLength = entityInsertions.Length;
-				int literalInsertionsInitialLength = literalInsertions.Length;
-				bool firstLiteralInsert = true, firstEntityInsert = true; // only used if insertCombined is true
 				
 				cmd = new StringBuilder();
 				if (insertCombined)
@@ -797,11 +592,11 @@
 				for (int i = 0; i < statements.Count; i++) {
 					Statement statement = (Statement)statements[i];
 				
-					int subj = GetResourceIdBuffer(statement.Subject, true, literalInsertions, entityInsertions, insertCombined, ref firstLiteralInsert, ref firstEntityInsert);
-					int pred = GetResourceIdBuffer(statement.Predicate, true,  literalInsertions, entityInsertions, insertCombined, ref firstLiteralInsert, ref firstEntityInsert);
+					int subj = GetResourceIdBuffer(statement.Subject, true, literalInsertions, entityInsertions, insertCombined);
+					int pred = GetResourceIdBuffer(statement.Predicate, true,  literalInsertions, entityInsertions, insertCombined);
 					int objtype = ObjectType(statement.Object);
-					int obj = GetResourceIdBuffer(statement.Object, true, literalInsertions, entityInsertions, insertCombined, ref firstLiteralInsert, ref firstEntityInsert);
-					int meta = GetResourceIdBuffer(statement.Meta, true, literalInsertions, entityInsertions, insertCombined, ref firstLiteralInsert, ref firstEntityInsert);
+					int obj = GetResourceIdBuffer(statement.Object, true, literalInsertions, entityInsertions, insertCombined);
+					int meta = GetResourceIdBuffer(statement.Meta, true, literalInsertions, entityInsertions, insertCombined);
 					
 					if (!insertCombined)
 						cmd.Append(INSERT_INTO_STATEMENTS_VALUES);
@@ -822,21 +617,22 @@
 						cmd.Append("),");
 				}
 				
-				if (literalInsertions.Length > literalInsertionsInitialLength) {
-					if (insertCombined)
+				if (literalInsertions.Length > 0) {
+					if (insertCombined) {
+						literalInsertions.Insert(0, INSERT_INTO_LITERALS_VALUES);
 						literalInsertions.Append(';');
-					if (Debug) Console.Error.WriteLine(literalInsertions.ToString());
+					}
 					RunCommand(literalInsertions.ToString());
 				}
 				
-				if (entityInsertions.Length > entityInsertionsInitialLength) {
-					if (insertCombined)
+				if (entityInsertions.Length > 0) {
+					if (insertCombined) {
+						entityInsertions.Insert(0, INSERT_INTO_ENTITIES_VALUES);
 						entityInsertions.Append(';');
-					if (Debug) Console.Error.WriteLine(entityInsertions.ToString());
+					}
 					RunCommand(entityInsertions.ToString());
 				}
 				
-				if (Debug) Console.Error.WriteLine(cmd.ToString());
 				RunCommand(cmd.ToString());
 			
 			} finally {
@@ -848,7 +644,7 @@
 			}
 		}
 		
-		public void Remove(Statement template) {
+		public override void Remove(Statement template) {
 			Init();
 			RunAddBuffer();
 
@@ -861,23 +657,18 @@
 			RunCommand(cmd.ToString());
 			
 			statementsRemoved = true;
+			metaEntities = null;
 		}
 		
-		public void RemoveAll(Statement[] templates) {
-			// TODO: Optimize this.
-			foreach (Statement t in templates)
-				Remove(t);
-		}
-		
-		public Entity[] GetEntities() {
+		public override Entity[] GetEntities() {
 			return GetAllEntities(fourcols);
 		}
 			
-		public Entity[] GetPredicates() {
+		public override Entity[] GetPredicates() {
 			return GetAllEntities(predcol);
 		}
 		
-		public Entity[] GetMetas() {
+		public override Entity[] GetMetas() {
 			return GetAllEntities(metacol);
 		}
 
@@ -912,13 +703,8 @@
 				if (!AppendMultiRes((MultiRes)r, cmd)) return false;
 				cmd.Append(" ))");
 			} else {
-				if (r.Uri != null && r.Uri == rdfs_member) {
-					if (CreateEntityPrefixTest(col, rdf_li, cmd)) return true;
-				}
-				
 				int id = GetResourceId(r, false);
 				if (id == 0) return false;
-				if (Debug) Console.Error.WriteLine("(" + id + " " + r + ")");
 				cmd.Append('(');
 				cmd.Append(col);
 				cmd.Append('=');
@@ -997,18 +783,6 @@
 			builder.Append(text);
 		}
 		
-		///////////////////////////
-		// QUERYING THE DATABASE //
-		///////////////////////////
-		
-		public bool Contains(Resource resource) {
-			return GetResourceId(resource, false) != 0;
-		}
-		
-		public bool Contains(Statement template) {
-			return Store.DefaultContains(this, template);
-		}
-
 		internal struct SelectColumnFilter {
 			public bool SubjectId, PredicateId, ObjectId, MetaId;
 			public bool SubjectUri, PredicateUri, ObjectData, MetaUri;
@@ -1053,12 +827,8 @@
 				cmd.Append("_entities AS muri ON q.meta = muri.id");
 			}
 		}
-		
-		public void Select(StatementSink result) {
-			Select(Statement.All, result);
-		}
 
-		public void Select(SelectFilter filter, StatementSink result) {
+		public override void Select(SelectFilter filter, StatementSink result) {
 			if (result == null) throw new ArgumentNullException();
 			foreach (Entity[] s in SplitArray(filter.Subjects))
 			foreach (Entity[] p in SplitArray(filter.Predicates))
@@ -1115,111 +885,13 @@
 			}
 		}
 		
-		void CleanMultiRes(MultiRes res) {
-			ArrayList newitems = new ArrayList();
-			foreach (Resource r in res.items)
-				if ((object)r == (object)Statement.DefaultMeta || GetResourceKey(r) != null)
-					newitems.Add(r);
-			res.items = (Resource[])newitems.ToArray(typeof(Resource));
-		}
-		
 		void CacheMultiObjects(Hashtable entMap, Resource obj) {
 			if (!(obj is MultiRes)) return;
 			foreach (Resource r in ((MultiRes)obj).items)
 				entMap[GetResourceId(r, false)] = r;
 		}
 		
-		bool isOrContains(Resource r, string uri) {
-			if (r == null) return false;
-			if (r is MultiRes) {
-				foreach (Resource rr in ((MultiRes)r).items)
-					if (isOrContains(rr, uri))
-						return true;
-			} else {
-				if (r.Uri != null && r.Uri == uri)
-					return true;
-			}
-			return false;
-		}
-		
-		void PrefetchResourceIds(IList resources) {
-			Hashtable seen_e = new Hashtable();
-			Hashtable seen_l = new Hashtable();
-			
-			int resStart = 0;
-			while (resStart < resources.Count) {
-
-			StringBuilder cmd_e = new StringBuilder();
-			cmd_e.Append("SELECT id, value FROM ");
-			cmd_e.Append(table);
-			cmd_e.Append("_entities WHERE value IN (");
-			bool hasEnts = false;
-
-			StringBuilder cmd_l = new StringBuilder();
-			cmd_l.Append("SELECT id, hash FROM ");
-			cmd_l.Append(table);
-			cmd_l.Append("_literals WHERE hash IN (");
-			bool hasLiterals = false;
-
-			int ctr = 0;
-			while (resStart < resources.Count && ctr < 1000) {
-				Resource r = (Resource)resources[resStart++];
-				
-				if ((object)r == (object)Statement.DefaultMeta || GetResourceKey(r) != null) // no need to prefetch
-					continue;
-					
-				ctr++;
-			
-				if (r.Uri != null) {
-					if (seen_e.ContainsKey(r.Uri)) continue;
-					if (hasEnts)
-						cmd_e.Append(" , ");
-					EscapedAppend(cmd_e, r.Uri);
-					hasEnts = true;
-					seen_e[r.Uri] = r;
-				}
-
-				Literal lit = r as Literal;
-				if (lit != null) {
-					string hash = GetLiteralHash(lit);
-					if (seen_l.ContainsKey(hash)) continue;
-					
-					if (hasLiterals)
-						cmd_l.Append(" , ");
-					cmd_l.Append(quote);
-					cmd_l.Append(hash);
-					cmd_l.Append(quote);
-					hasLiterals = true;
-					seen_l[hash] = lit;
-				}
-			}
-			if (hasEnts) {
-				cmd_e.Append(");");
-				if (Debug) Console.Error.WriteLine(cmd_e.ToString());
-				using (IDataReader reader = RunReader(cmd_e.ToString())) {
-					while (reader.Read()) {
-						int id = reader.GetInt32(0);
-						string uri = AsString(reader[1]);
-						SetResourceKey((Entity)seen_e[uri], new ResourceKey(id));
-					}
-				}
-			}
-				
-			if (hasLiterals) {
-				cmd_l.Append(");");
-				using (IDataReader reader = RunReader(cmd_l.ToString())) {
-					while (reader.Read()) {
-						int id = reader.GetInt32(0);
-						string hash = AsString(reader[1]);
-						SetResourceKey((Literal)seen_l[hash], new ResourceKey(id));
-					}
-				}
-			}
-			
-			}
-		}
-		
-		public void Select(Statement template, StatementSink result) {
+		public override void Select(Statement template, StatementSink result) {
 			if (result == null) throw new ArgumentNullException();
 			Select(template.Subject, template.Predicate, template.Object, template.Meta, null, result, 0);
 		}
@@ -1244,46 +916,26 @@
 			columns.ObjectData = templateObject == null || (templateObject is MultiRes && ((MultiRes)templateObject).ContainsLiterals());
 			columns.MetaUri = templateMeta == null;
 			
-			if (isOrContains(templatePredicate, rdfs_member)) {
-				columns.PredicateId = true;
-				columns.PredicateUri = true;
-			}
-			
 			// Meta URIs tend to be repeated a lot, so we don't
 			// want to ever select them from the database.
 			// This preloads them, although it makes the first
 			// select quite slow.
-			/*if (templateMeta == null && SupportsSubquery) {
+			if (templateMeta == null && SupportsSubquery) {
 				LoadMetaEntities();
 				columns.MetaUri = false;
-			}*/
+			}
 			
 			// Have to select something
 			if (!columns.SubjectId && !columns.PredicateId && !columns.ObjectId && !columns.MetaId)
 				columns.SubjectId = 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 }) {
-				MultiRes mr = r as MultiRes;
-				if (mr == null) continue;
-				PrefetchResourceIds(mr.items);
-				CleanMultiRes(mr);
-				if (mr.items.Length == 0) // no possible values
-					return;
-			}
-				
 			// SQLite has a problem with LEFT JOIN: When a condition is made on the
 			// first table in the ON clause (q.objecttype=0/1), when it fails,
 			// it excludes the row from the first table, whereas it should only
 			// 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)
+			if (!SupportsNoDuplicates)
 				cmd.Append("DISTINCT ");
 			SelectFilterColumns(columns, cmd);
 			cmd.Append(" FROM ");
@@ -1308,7 +960,7 @@
 				}
 			}
 			
-			if (SupportsLimitClause && limit >= 1) {
+			if (limit >= 1) {
 				cmd.Append(" LIMIT ");
 				cmd.Append(limit);
 			}
@@ -1351,7 +1003,7 @@
 					Entity subject = GetSelectedEntity(sid, suri, templateSubject, columns.SubjectId, 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);
+					Entity meta = GetSelectedEntity(mid, muri, templateMeta, columns.MetaId, columns.MetaUri, templateMeta != null ? entMap : metaEntities);
 
 					if (litFilters != null && !LiteralFilter.MatchesFilters(objec, litFilters, this))
 						continue;
@@ -1363,418 +1015,7 @@
 			
 			} // lock
 		}
-		
-		public SemWeb.Query.MetaQueryResult MetaQuery(Statement[] graph, SemWeb.Query.QueryOptions options) {
-			return new SemWeb.Inference.SimpleEntailment().MetaQuery(graph, options, this);
-		}
-		
-		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");
-
-			options = options.Clone(); // because we modify the knownvalues array
-			
-			// Order the variables mentioned in the graph.
-			Variable[] varOrder;
-			ResSet distinguishedVars = null;
-			bool useDistinct = false;
-			{
-				if (options.DistinguishedVariables != null)
-					distinguishedVars = new ResSet(options.DistinguishedVariables);
-				else
-					distinguishedVars = new ResSet();
-			
-				Hashtable seenvars = new Hashtable();
-				foreach (Statement filter in graph) {
-					for (int i = 0; i < 4; i++) {
-						Resource r = filter.GetComponent(i);
-						if (r == null)
-							throw new ArgumentException("The graph may not have any null components.  Use Variables instead.");
-
-						if (r is Variable) {
-							if (options.DistinguishedVariables != null) {
-								if (!distinguishedVars.Contains(r)) {
-									// If we are omitting a column from the results because it is
-									// not distinguished, and it's not a meta column, then we'll
-									// use DISTINCT.
-									if (i != 3)
-										useDistinct = true;
-										
-									// Don't put this into seenvars.
-									continue;
-								}
-							} else {
-								distinguishedVars.Add(r); // all variables are distinguished
-							}
-							
-							seenvars[r] = r;
-						}
-					}
-				}
-				
-				varOrder = new Variable[seenvars.Count];
-				int ctr = 0;
-				foreach (Variable v in seenvars.Keys)
-					varOrder[ctr++] = v;
-			}
-			
-			bool useView = useDistinct && SupportsViews && !NoSQLView;
-			
-			// Set the initial bindings to the result sink
-
-			sink.Init(varOrder);
-			
-			Hashtable varLitFilters = new Hashtable();
-			
-			// Prefetch the IDs of all resources mentioned in the graph and in variable known values.
-			// For Resources in the graph that are not in the store, the query immediately fails.
-			{
-				ArrayList graphResources = new ArrayList();
-				foreach (Statement s in graph) {
-					for (int i = 0; i < 4; i++) {
-						Resource r = s.GetComponent(i);
-						if (!(r is BNode)) // definitely exclude variables, but bnodes are useless too
-							graphResources.Add(r);
-					}
-				}
-				if (options.VariableKnownValues != null)
-					foreach (ICollection values in options.VariableKnownValues.Values)
-						graphResources.AddRange(values);
-
-				PrefetchResourceIds(graphResources);
-				
-				// Check resources in graph and fail fast if any is not in the store.
-				foreach (Statement s in graph) {
-					for (int i = 0; i < 4; i++) {
-						Resource r = s.GetComponent(i);
-						if (r is Variable) continue;
-						if ((object)r != (object)Statement.DefaultMeta && GetResourceKey(r) == null) {
-							sink.AddComments("Resource " + r + " is not contained in the data model.");
-							sink.Finished();
-							return;
-						}
-					}
-				}
-				
-				// Check variable known values and remove any values not in the store.
-				// Don't do any fail-fasting here because there might be entries in this
-				// dictionary that aren't even used in this query (yes, poor design).
-				// We check later anyway.
-				if (options.VariableKnownValues != null) {
-					#if !DOTNET2
-					foreach (Variable v in new ArrayList(options.VariableKnownValues.Keys)) {
-					#else
-					foreach (Variable v in new System.Collections.Generic.List<Variable>(options.VariableKnownValues.Keys)) {
-					#endif
-						#if !DOTNET2
-						ArrayList newvalues = new ArrayList();
-						#else
-						System.Collections.Generic.List<Resource> newvalues = new System.Collections.Generic.List<Resource>();
-						#endif
-						
-						foreach (Resource r in (ICollection)options.VariableKnownValues[v]) {
-							if ((object)r == (object)Statement.DefaultMeta || GetResourceKey(r) != null)
-								newvalues.Add(r);
-						}
-
-						options.VariableKnownValues[v] = newvalues;
-					}
-				}
-			}
-			
-			// Helpers
-			
-			string[] colnames = { "subject", "predicate", "object", "meta" };
-			
-			// Lock the store and make sure we are initialized and any pending add's have been committed. 
-					
-			lock (syncroot) {
-			
-			Init();
-			RunAddBuffer();
-			
-			// 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();
-			
-			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
-				// alias: q#, where # is the filter's index.
-				
-				if (f > 0) fromClause.Append(',');
-				fromClause.Append(table);
-				fromClause.Append("_statements AS g");
-				fromClause.Append(f);
-				
-				// For each component of the filter...
-				
-				for (int i = 0; i < 4; i++) {
-					string myRef = "g" + f + "." + colnames[i];
-					
-					Variable v = graph[f].GetComponent(i) as Variable;
-					if (v != null) {
-						// If the component is a variable, then if this is
-						// the first time we're seeing the variable, we don't
-						// add any restrictions to the WHERE clause, but we
-						// note the variable's "name" in the world of SQL
-						// so we can refer back to it later and we add the
-						// necessary FROM tables so we can get its URI and
-						// literal value if it is a reported variable.
-						// If this isn't the first time, then we add a WHERE restriction so
-						// 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);
-							
-							int vIndex = varRef_Inner.Count;
-							varRef2[v] = vIndex;
-							
-							#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 ");
-								}
-							}
-							
-							if (options.VariableKnownValues != null) {
-								ICollection values = null;
-								#if DOTNET2
-								if (options.VariableKnownValues.ContainsKey(v))
-								#endif
-									values = (ICollection)options.VariableKnownValues[v];
-								if (values != null) {
-									if (values.Count == 0) {
-										sink.Finished();
-										return;
-									}
-									Resource r = ToMultiRes((Resource[])new ArrayList(values).ToArray(typeof(Resource)));
-									if (!WhereItem(myRef, r, whereClause, whereClause.Length != 0)) {
-										// We know at this point that the query cannot return any results.
-										sink.Finished();
-										return;
-									}
-								}
-							}
-							
-						} else {
-							if (whereClause.Length != 0) whereClause.Append(" AND ");
-							whereClause.Append('(');
-							whereClause.Append((string)varRef_Inner[v]);
-							whereClause.Append('=');
-							whereClause.Append(myRef);
-							whereClause.Append(')');
-						}
-					
-					} else {
-						// If this is not a variable, then it is a resource.
-					
-						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();
-							return;
-						}
-
-					}
-				}
-			
-			} // graph filter 0...n
-			
-			// Add literal filters to the WHERE clause
-
-			foreach (Variable v in varOrder) {
-				// Is there a literal value filter?
-				if (options.VariableLiteralFilters == null) continue;
-				#if !DOTNET2
-				if (options.VariableLiteralFilters[v] == null) continue;
-				#else
-				if (!options.VariableLiteralFilters.ContainsKey(v)) continue;
-				#endif
-				
-				// If this variable was not used in a literal column, then
-				// we cannot filter its value. Really, it will never be a literal.
-				if (!(bool)varSelectedLiteral[v]) continue;
-
-				foreach (LiteralFilter filter in (ICollection)options.VariableLiteralFilters[v]) {
-					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);
-				}
-			}
-
-			// 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]]);
-				
-				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");
-				}
-			}
-			
-			cmd.Append(" FROM ");
-			cmd.Append(fromClause.ToString());
-			
-			if (whereClause.Length > 0)
-				cmd.Append(" WHERE ");
-			cmd.Append(whereClause.ToString());
-			
-			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 {
-			using (IDataReader reader = RunReader(cmd.ToString())) {
-				while (reader.Read()) {
-					Resource[] variableBindings = new Resource[varOrder.Length];
-				
-					int col = 0;
-					for (int i = 0; i < varOrder.Length; i++) {
-						int id = reader.GetInt32(col++);
-						string uri = AsString(reader[col++]);
-						
-						string litvalue = null, litlanguage = null, litdatatype = null;
-
-						if ((bool)varSelectedLiteral[varOrder[i]]) {
-							litvalue = AsString(reader[col++]);
-							litlanguage = AsString(reader[col++]);
-							litdatatype = 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 (!sink.Add(new SemWeb.Query.VariableBindings(varOrder, variableBindings))) return;
-				}
-			}
-			} finally {
-				if (useView)
-					RunCommand("DROP VIEW " + viewname);
-
-				sink.Finished();
-			}
-				
-			} // lock
-		}
-		
 		Entity GetSelectedEntity(int id, string uri, Resource given, bool idSelected, bool uriSelected, Hashtable entMap) {
 			if (!idSelected) return (Entity)given;
 			if (!uriSelected) {
@@ -1795,12 +1036,6 @@
 			else
 				return new Literal(lv, ll, ld);
 		}
-		
-		private string CreateLikeTest(string column, string match, int method) {
-			StringBuilder s = new StringBuilder();
-			CreateLikeTest(column, match, method, s);
-			return s.ToString();
-		}
 
 		private string FilterToSQL(LiteralFilter filter, string col) {
 			if (filter is SemWeb.Filters.StringCompareFilter) {
@@ -1809,15 +1044,11 @@
 			}
 			if (filter is SemWeb.Filters.StringContainsFilter) {
 				SemWeb.Filters.StringContainsFilter f = (SemWeb.Filters.StringContainsFilter)filter;
-				return CreateLikeTest(col, f.Pattern, 1); // 1=contains
+				return col + " LIKE " + quote + "%" + Escape(f.Pattern, false).Replace("%", "\\%") + "%" + quote;
 			}
 			if (filter is SemWeb.Filters.StringStartsWithFilter) {
 				SemWeb.Filters.StringStartsWithFilter f = (SemWeb.Filters.StringStartsWithFilter)filter;
-				return CreateLikeTest(col, f.Pattern, 0); // 0=starts-with
-			}
-			if (filter is SemWeb.Filters.StringEndsWithFilter) {
-				SemWeb.Filters.StringEndsWithFilter f = (SemWeb.Filters.StringEndsWithFilter)filter;
-				return CreateLikeTest(col, f.Pattern, 2); // 2==ends-with
+				return col + " LIKE " + quote + Escape(f.Pattern, false).Replace("%", "\\%") + "%" + quote;
 			}
 			if (filter is SemWeb.Filters.NumericCompareFilter) {
 				SemWeb.Filters.NumericCompareFilter f = (SemWeb.Filters.NumericCompareFilter)filter;
@@ -1838,21 +1069,34 @@
 			}			
 		}
 		
+		private void LoadMetaEntities() {
+			if (metaEntities != null) return;
+			metaEntities = new Hashtable();
+			// this misses meta entities that are anonymous, but that's ok
+			using (IDataReader reader = RunReader("select id, value from " + table + "_entities where id in (select distinct meta from " + table + "_statements)")) {
+				while (reader.Read()) {
+					int id = reader.GetInt32(0);
+					string uri = reader.GetString(1);
+					metaEntities[id] = MakeEntity(id, uri, null);
+				}
+			}
+		}
+		
 		private string Escape(string str, bool quotes) {
 			if (str == null) return "NULL";
 			StringBuilder b = new StringBuilder();
-			EscapedAppend(b, str, quotes, false);
+			EscapedAppend(b, str, quotes);
 			return b.ToString();
 		}
 		
 		protected void EscapedAppend(StringBuilder b, string str) {
-			EscapedAppend(b, str, true, false);
+			EscapedAppend(b, str, true);
 		}
 
 		protected virtual char GetQuoteChar() {
 			return '\"';
 		}
-		protected virtual void EscapedAppend(StringBuilder b, string str, bool quotes, bool forLike) {
+		protected virtual void EscapedAppend(StringBuilder b, string str, bool quotes) {
 			if (quotes) b.Append(quote);
 			for (int i = 0; i < str.Length; i++) {
 				char c = str[i];
@@ -1861,16 +1105,9 @@
 					case '\\':
 					case '\"':
 					case '*':
-					case '\'':
 						b.Append('\\');
 						b.Append(c);
 						break;
-					case '%':
-					case '_':
-						if (forLike)
-							b.Append('\\');
-						b.Append(c);
-						break;
 					default:
 						b.Append(c);
 						break;
@@ -1887,7 +1124,7 @@
 			b.Replace("*", "\\*");
 		}*/
 
-		public void Import(StatementSource source) {
+		public override void Import(StatementSource source) {
 			if (source == null) throw new ArgumentNullException();
 			if (isImporting) throw new InvalidOperationException("Store is already importing.");
 			
@@ -1895,15 +1132,13 @@
 			RunAddBuffer();
 			
 			cachedNextId = -1;
-			NextId(); // get this before starting transaction because it relies on indexes which may be disabled
-			
 			addStatementBuffer = new StatementList();
 			
 			BeginTransaction();
 			
 			try {
 				isImporting = true;
-				source.Select(this);
+				base.Import(source);
 			} finally {
 				RunAddBuffer();
 				EndTransaction();
@@ -1916,7 +1151,7 @@
 			}
 		}
 
-		public void Replace(Entity a, Entity b) {
+		public override void Replace(Entity a, Entity b) {
 			Init();
 			RunAddBuffer();
 			int id = GetResourceId(b, true);
@@ -1934,9 +1169,10 @@
 				RunCommand(cmd.ToString());
 			}
 
+			metaEntities = null;
 		}
 		
-		public void Replace(Statement find, Statement replacement) {
+		public override void Replace(Statement find, Statement replacement) {
 			if (find.AnyNull) throw new ArgumentNullException("find");
 			if (replacement.AnyNull) throw new ArgumentNullException("replacement");
 			if (find == replacement) return;
@@ -1970,16 +1206,9 @@
 				return;
 			
 			RunCommand(cmd.ToString());
+			metaEntities = null;
 		}
 		
-		private object GetResourceKey(Resource resource) {
-			return resource.GetResourceKey(this);
-		}
-
-		private void SetResourceKey(Resource resource, object value) {
-			resource.SetResourceKey(this, value);
-		}
-
 		protected abstract void RunCommand(string sql);
 		protected abstract object RunScalar(string sql);
 		protected abstract IDataReader RunReader(string sql);
@@ -1990,7 +1219,7 @@
 			if (ret is int) return (int)ret;
 			try {
 				return int.Parse(ret.ToString());
-			} catch (FormatException) {
+			} catch (FormatException e) {
 				return def;
 			}
 		}
@@ -2003,11 +1232,7 @@
 			throw new FormatException("SQL store returned a literal value as " + ret);
 		}
 		
-		void IDisposable.Dispose() {
-			Close();
-		}
-		
-		public virtual void Close() {
+		public override void Close() {
 			if (statementsRemoved) {
 				RunCommand("DELETE FROM " + table + "_literals where (select count(*) from " + table + "_statements where object=id) = 0 and id > 0");
 				RunCommand("DELETE FROM " + table + "_entities where (select count(*) from " + table + "_statements where subject=id) = 0 and (select count(*) from " + table + "_statements where predicate=id) = 0 and (select count(*) from " + table + "_statements where object=id) = 0 and (select count(*) from " + table + "_statements where meta=id) = 0 ;");
@@ -2019,7 +1244,7 @@
 				try {
 					RunCommand(cmd);
 				} catch (Exception e) {
-					if (Debug && e.Message.IndexOf("already exists") == -1) Console.Error.WriteLine(e);
+					if (Debug) Console.Error.WriteLine(e);
 				}
 			}
 		}
@@ -2053,12 +1278,11 @@
 		internal static string[] GetCreateIndexCommands(string table) {
 			return new string[] {
 				"CREATE UNIQUE INDEX subject_full_index ON " + table + "_statements(subject, predicate, object, meta, objecttype);",
-				"CREATE INDEX predicate_index ON " + table + "_statements(predicate, object);",
+				"CREATE INDEX predicate_index ON " + table + "_statements(predicate);",
 				"CREATE INDEX object_index ON " + table + "_statements(object);",
 				"CREATE INDEX meta_index ON " + table + "_statements(meta);",
 			
 				"CREATE UNIQUE INDEX literal_index ON " + table + "_literals(hash);",
-				"CREATE INDEX literal_value_index ON " + table + "_literals(value(20));",
 				"CREATE UNIQUE INDEX entity_index ON " + table + "_entities(value(255));"
 				};
 		}

Modified: trunk/semweb/Statement.cs
==============================================================================
--- trunk/semweb/Statement.cs	(original)
+++ trunk/semweb/Statement.cs	Fri May 16 18:52:24 2008
@@ -4,14 +4,7 @@
 using SemWeb.Util;
 
 namespace SemWeb {
-	public struct Statement :
-#if DOTNET2
-	IEquatable<Statement>, IComparable<Statement>
-#else
-	IComparable
-#endif
-	{
-	
+	public struct Statement : IComparable {
 		public Entity Subject;
 		public Entity Predicate;
 		public Resource Object;
@@ -84,18 +77,11 @@
 				!resourceMap.ContainsKey(Meta) ? Meta : (Entity)resourceMap[Meta]
 				);
 		}
-
 
 		public override bool Equals(object other) {
 			return (Statement)other == this;
-		}
-		
-#if DOTNET2
-		bool IEquatable<Statement>.Equals(Statement other) {
-			return other == this;
 		}
-#endif
-
+		
 		public override int GetHashCode() {
 			int ret = 0;
 			if (Subject != null) ret = unchecked(ret + Subject.GetHashCode());
@@ -120,13 +106,8 @@
 			return !(a == b);
 		}
 
-#if !DOTNET2
-		int IComparable.CompareTo(object other) {
-			return CompareTo((Statement)other);
-		}
-#endif
-
-		public int CompareTo(Statement s) {
+		int IComparable.CompareTo(object obj) {
+			Statement s = (Statement)obj;
 			int x;
 			x = cmp(Subject, s.Subject); if (x != 0) return x;
 			x = cmp(Predicate, s.Predicate); if (x != 0) return x;
@@ -137,7 +118,7 @@
 		int cmp(Resource a, Resource b) {
 			if (a == null && b == null) return 0;
 			if (a == null) return -1;
-			if (b == null) return 1;
+			if (b == null) return 1;
 			return ((IComparable)a).CompareTo(b);
 		}
 		
@@ -150,15 +131,6 @@
 			}
 			throw new ArgumentException("index");
 		}
-		internal void SetComponent(int index, Resource r) {
-			switch (index) {
-				case 0: Subject = (Entity)r; break;
-				case 1: Predicate = (Entity)r; break;
-				case 2: Object = r; break;
-				case 3: Meta = (Entity)r; break;
-				default: throw new ArgumentException("index");
-			}
-		}
 	}
 	
 	public struct SelectFilter : IEnumerable {
@@ -187,26 +159,6 @@
 			Metas = metas;
 		}
 		
-		internal Resource[] GetComponent(int index) {
-			switch (index) {
-				case 0: return Subjects;
-				case 1: return Predicates;
-				case 2: return Objects;
-				case 3: return Metas;
-			}
-			throw new ArgumentException("index");
-		}
-		
-		internal void SetComponent(int index, Resource[] res) {
-			switch (index) {
-				case 0: Subjects = (Entity[])res; break;
-				case 1: Predicates = (Entity[])res; break;
-				case 2: Objects = res; break;
-				case 3: Metas = (Entity[])res; break;
-				default: throw new ArgumentException("index");
-			}
-		}
-
 		public override string ToString() {
 			string ret =
 				ToString(Subjects) + " " +
@@ -252,13 +204,13 @@
 				&& eq(a.Predicates, b.Predicates)
 				&& eq(a.Objects, b.Objects)
 				&& eq(a.Metas, b.Metas)
-				&& a.LiteralFilters == b.LiteralFilters
+				&& eq(a.LiteralFilters, b.LiteralFilters)
 				&& a.Limit == b.Limit;
 		}
 		public static bool operator !=(SelectFilter a, SelectFilter b) {
 			return !(a == b);
 		}
-		static bool eq(Resource[] a, Resource[] b) {
+		static bool eq(object[] a, object[] b) {
 			if (a == b) return true;
 			if (a == null || b == null) return false;
 			if (a.Length != b.Length) return false;

Modified: trunk/semweb/Store.cs
==============================================================================
--- trunk/semweb/Store.cs	(original)
+++ trunk/semweb/Store.cs	Fri May 16 18:52:24 2008
@@ -1,978 +1,592 @@
-using System;
-#if !DOTNET2
-using System.Collections;
-#else
-using System.Collections.Generic;
-#endif
-using System.Data;
+using System;
+using System.Collections;
+using System.Data;
 
-using SemWeb.Inference;
 using SemWeb.Util;
-
-#if !DOTNET2
-using SourceList = System.Collections.ArrayList;
-using NamedSourceMap = System.Collections.Hashtable;
-using ReasonerList = System.Collections.ArrayList;
-#else
-using SourceList = System.Collections.Generic.List<SemWeb.SelectableSource>;
-using NamedSourceMap = System.Collections.Generic.Dictionary<string, SemWeb.SelectableSource>;
-using ReasonerList = System.Collections.Generic.List<SemWeb.Inference.Reasoner>;
-#endif
-
-namespace SemWeb {
-	
-	public class Store : StatementSource, StatementSink,
-		SelectableSource, QueryableSource, StaticSource, ModifiableSource,
-		IDisposable {
-		
-		// Static helper methods for creating data sources and sinks
-		// from spec strings.
-		
-		public static Store Create(string spec) {
-			Store ret = new Store();
-			
-			bool rdfs = false, euler = false;
-			
-			if (spec.StartsWith("rdfs+")) {
-				rdfs = true;
-				spec = spec.Substring(5);
-			}
-			if (spec.StartsWith("euler+")) {
-				euler = true;
-				spec = spec.Substring(6);
-			}
-				
-			foreach (string spec2 in spec.Split('\n', '|')) {
-				StatementSource s = CreateForInput(spec2.Trim());
-				if (s is SelectableSource)
-					ret.AddSource((SelectableSource)s);
-				else
-					ret.AddSource(new MemoryStore(s));
-			}
-			
-			if (rdfs)
-				ret.AddReasoner(new RDFS(ret));
-			if (euler)
-				ret.AddReasoner(new Euler(ret)); // loads it all into memory!
-			
-			return ret;
-		}
-		
-		public static StatementSource CreateForInput(string spec) {
-			if (spec.StartsWith("debug+")) {
-				StatementSource s = CreateForInput(spec.Substring(6));
-				if (!(s is SelectableSource)) s = new MemoryStore(s);
-				return new SemWeb.Stores.DebuggedSource((SelectableSource)s, System.Console.Error);
-			}
-			return (StatementSource)Create(spec, false);
-		}		
-		
-		public static StatementSink CreateForOutput(string spec) {
-			return (StatementSink)Create(spec, true);
-		}
-		
-		private static object Create(string spec, bool output) {
-			string type = spec;
-			
-			int c = spec.IndexOf(':');
-			if (c != -1) {
-				type = spec.Substring(0, c);
-				spec = spec.Substring(c+1);
-			} else {
-				spec = "";
-			}
-			
-			Type ttype;
-			
-			switch (type) {
-				case "mem":
-					return new MemoryStore();
-				case "xml":
-					if (spec == "") throw new ArgumentException("Use: xml:filename");
-					if (output) {
-						#if !SILVERLIGHT
-							return new RdfXmlWriter(spec);
-						#else
-							throw new NotSupportedException("RDF/XML output is not supported in the Silverlight build of the SemWeb library.");
-						#endif
-					} else {
-						return new RdfXmlReader(spec);
-					}
-				case "n3":
-				case "ntriples":
-				case "nt":
-				case "turtle":
-					if (spec == "") throw new ArgumentException("Use: format:filename");
-					if (output) {
-						N3Writer ret = new N3Writer(spec); // turtle is default format
-						switch (type) {
-							case "nt": case "ntriples":
-								ret.Format = N3Writer.Formats.NTriples;
-								break;
-						}
-						return ret;
-					} else {
-						return new N3Reader(spec);
-					}
-				
-				case "null":
-					if (!output) throw new ArgumentException("The null sink does not support reading.");
-					return new StatementCounterSink();
-				
-				/*case "file":
-					if (spec == "") throw new ArgumentException("Use: format:filename");
-					if (output) throw new ArgumentException("The FileStore does not support writing.");
-					return new SemWeb.Stores.FileStore(spec);*/
-
-				case "sqlite":
-				case "mysql":
-				case "postgresql":
-				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).");
-					string table = spec.Substring(0, c);
-					spec = spec.Substring(c+1);
-					
-					string classtype = null;
-					if (type == "sqlite") {
-						classtype = "SemWeb.Stores.SqliteStore, SemWeb.SqliteStore";
-						spec = spec.Replace(";", ",");
-					} else if (type == "mysql") {
-						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)
-						throw new NotSupportedException("The storage type in <" + classtype + "> could not be found.");
-					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 "class":
-					ttype = Type.GetType(spec);
-					if (ttype == null)
-						throw new NotSupportedException("The class <" + spec + "> could not be found.");
-					return Activator.CreateInstance(ttype);
-				default:
-					throw new ArgumentException("Unknown parser type: " + type);
-			}
-		}
-		
-		// START OF ACTUAL STORE IMPLEMENTATION
-		
-		readonly Entity rdfType = new Entity("http://www.w3.org/1999/02/22-rdf-syntax-ns#type";);
-
-		SourceList unnamedgraphs = new SourceList(); // a list of SelectableSources that aren't associated with graph URIs
-		NamedSourceMap namedgraphs = new NamedSourceMap(); // a mapping from graph URIs to a selectable source that represents that graph
-		
-		SourceList allsources = new SourceList(); // a list of the sources in unnamed graphs and namedgraphs
-		
-		ReasonerList reasoners = new ReasonerList(); // a list of reasoning engines applied to this data model, which are run in order
-		
-		// GENERAL METHODS
-		
-		public Store() {
-		}
-		
-		public Store(StatementSource source) {
-			AddSource(new MemoryStore.StoreImpl(source));
-		}
-		
-		public Store(SelectableSource source) {
-			AddSource(source);
-		}
-		
-		#if !DOTNET2
-		public IList DataSources {
-			get {
-				return ArrayList.ReadOnly(allsources);
-			}
-		}
-		#else
-		public IList<SelectableSource> DataSources {
-			get {
-				return new List<SelectableSource>(allsources);
-			}
-		}
-		#endif
-				
-		public virtual void AddSource(SelectableSource source) {
-			if (source is MemoryStore) source = ((MemoryStore)source).impl;
-			unnamedgraphs.Add(source);
-			allsources.Add(source);
-		}
-		
-		public virtual void AddSource(SelectableSource source, string uri) {
-			if (namedgraphs.ContainsKey(uri))
-				throw new ArgumentException("URI has already been associated with a data source.");
-			if (source is MemoryStore) source = ((MemoryStore)source).impl;
-			namedgraphs[uri] = source;
-			allsources.Add(source);
-		}
-		
-		internal void AddSource2(SelectableSource store) {
-			// Used by MemoryStore only!
-			unnamedgraphs.Add(store);
-			allsources.Add(store);
-		}
-		
-		public virtual void AddReasoner(Reasoner reasoner) {
-			reasoners.Add(reasoner);
-		}
-		
-		public void Write(System.IO.TextWriter writer) {
-			using (RdfWriter w = new N3Writer(writer)) {
-				Select(w);
-			}
-		}
-
-		// INTERFACE IMPLEMENTATIONS and related methods
-		
-		// IDisposable
-		
-		public void Dispose() {
-			foreach (SelectableSource s in allsources)
-				if (s is IDisposable)
-					((IDisposable)s).Dispose();
-			foreach (Reasoner r in reasoners)
-				if (r is IDisposable)
-					((IDisposable)r).Dispose();
-		}
-		
-		// StatementSource
-		
-		public bool Distinct {
-			get {
-				if (allsources.Count > 1) return false;
-				foreach (Reasoner r in reasoners)
-					if (!r.Distinct)
-						return false;
-				if (allsources.Count == 0) return true;
-				return ((SelectableSource)allsources[0]).Distinct;
-			}
-		}
-		
-		public void Select(StatementSink result) {
-			Select(Statement.All, result);
-		}
-
-		// SelectableSource
-		
-		private SelectableSource[] GetSources(ref Entity graph) {
-			if (graph == null || namedgraphs.Count == 0)
-				#if !DOTNET2
-					return (SelectableSource[])allsources.ToArray(typeof(SelectableSource));
-				#else
-					return allsources.ToArray();
-				#endif
-			else if (graph == Statement.DefaultMeta)
-				#if !DOTNET2
-					return (SelectableSource[])unnamedgraphs.ToArray(typeof(SelectableSource));
-				#else
-					return unnamedgraphs.ToArray();
-				#endif
-			else if (graph.Uri != null && namedgraphs.ContainsKey(graph.Uri)) {
-				graph = Statement.DefaultMeta;
-				return new SelectableSource[] { (SelectableSource)namedgraphs[graph.Uri] };
-			} else
-				return null;
-		}
-
-		public bool Contains(Resource resource) {
-			foreach (SelectableSource s in allsources)
-				if (s.Contains(resource))
-					return true;
-			return false;
-			/*return (resource is Entity && Contains(new Statement((Entity)resource, null, null, null)))
-				|| (resource is Entity && Contains(new Statement(null, (Entity)resource, null, null)))
-				|| (                      Contains(new Statement(null, null, resource, null)))
-				|| (resource is Entity && Contains(new Statement(null, null, null, (Entity)resource)));*/
-		}
-		
-		public bool Contains(Statement template) {
-			// If reasoning is applied, use DefaultContains so that
-			// we use a Select call which will delegate the query
-			// to the reasoner.
-			ReasoningHelper rh = GetReasoningHelper(null);
-			if (rh != null)
-				return DefaultContains(this, template);
-			
-			SelectableSource[] sources = GetSources(ref template.Meta);
-			if (sources == null) return false;
-			foreach (SelectableSource s in sources)
-				if (s.Contains(template))
-					return true;
-			return false;
-		}
-			
-		public static bool DefaultContains(SelectableSource source, Statement template) {
-			StatementExistsSink sink = new StatementExistsSink();
-			SelectFilter filter = new SelectFilter(template);
-			filter.Limit = 1;
-			source.Select(filter, sink);
-			return sink.Exists;
-		}
-		
-		private class ReasoningHelper {
-			public Reasoner reasoner;
-			public Store nextStore;
-		}
-		
-		private ReasoningHelper GetReasoningHelper(SelectableSource[] sources) {
-			if (reasoners.Count == 0)
-				return null;
-		
-			ReasoningHelper ret = new ReasoningHelper();
-			
-			ret.reasoner = (Reasoner)reasoners[reasoners.Count-1];
-			
-			ret.nextStore = new Store();
-			if (sources == null) {
-				ret.nextStore.unnamedgraphs = unnamedgraphs; // careful...
-				ret.nextStore.namedgraphs = namedgraphs;
-				ret.nextStore.allsources = allsources;
-			} else {
-				ret.nextStore.unnamedgraphs.AddRange(sources);
-				ret.nextStore.allsources.AddRange(sources);
-			}
-			for (int i = 0; i < reasoners.Count-1; i++)
-				ret.nextStore.reasoners.Add(reasoners[i]);
-			return ret;
-		}
-		
-		public void Select(Statement template, StatementSink result) {
-			// If reasoning is applied, delegate this call to the last reasoner
-			// and pass it a clone of this store but with itself removed.
-			ReasoningHelper rh = GetReasoningHelper(null);
-			if (rh != null) {
-				rh.reasoner.Select(template, rh.nextStore, result);
-				return;
-			}
-		
-			SelectableSource[] sources = GetSources(ref template.Meta);
-			if (sources == null) return;
-			foreach (SelectableSource s in sources)
-				s.Select(template, result);
-		}
-		
-		public void Select(SelectFilter filter, StatementSink result) {
-			Entity[] scanMetas = filter.Metas;
-			if (scanMetas == null || namedgraphs.Count == 0) scanMetas = new Entity[] { null };
-			foreach (Entity meta in scanMetas) {
-				Entity meta2 = meta;
-				SelectableSource[] sources = GetSources(ref meta2);
-				if (sources == null) continue;
-				
-				if (meta2 == null)
-					filter.Metas = null;
-				else
-					filter.Metas = new Entity[] { meta2 };
-
-				// If reasoning is applied, delegate this call to the last reasoner
-				// and pass it either:
-				// 		a clone of this store but with itself removed, if the meta we are processing now is null, or
-				//		that, but only with the sources that apply to this meta
-				ReasoningHelper rh = GetReasoningHelper(sources);
-				if (rh != null) {
-					rh.reasoner.Select(filter, rh.nextStore, result);
-					continue;
-				}
-				
-				foreach (SelectableSource s in sources)
-					s.Select(filter, result);
-			}
-		}
-		
-		public static void DefaultSelect(SelectableSource source, SelectFilter filter, StatementSink sink) {
-			// This method should really be avoided...
-			if (filter.LiteralFilters != null)
-				sink = new SemWeb.Filters.FilterSink(filter.LiteralFilters, sink, source);
-			foreach (Entity subject in filter.Subjects == null ? new Entity[] { null } : filter.Subjects)
-			foreach (Entity predicate in filter.Predicates == null ? new Entity[] { null } : filter.Predicates)
-			foreach (Resource objct in filter.Objects == null ? new Resource[] { null } : filter.Objects)
-			foreach (Entity meta in filter.Metas == null ? new Entity[] { null } : filter.Metas)
-				source.Select(new Statement(subject, predicate, objct, meta), sink);
-		}		
-		
-		public SelectResult Select(Statement template) {
-			return new SelectResult.Single(this, template);
-		}
-		
-		public SelectResult Select(SelectFilter filter) {
-			return new SelectResult.Multi(this, filter);
-		}
-		
-		public Resource[] SelectObjects(Entity subject, Entity predicate) {
-			if (predicate.Uri != null && predicate.Uri == NS.RDFS + "member") {
-				ResourceCollector2 collector = new ResourceCollector2();
-				Select(new Statement(subject, predicate, null, null), collector);
-				return collector.GetItems();
-			} else {
-				ResSet resources = new ResSet();
-				ResourceCollector collector = new ResourceCollector();
-				collector.SPO = 2;
-				collector.Table = resources;
-				Select(new Statement(subject, predicate, null, null), collector);
-				return resources.ToArray();
-			}
-		}
-		public Entity[] SelectSubjects(Entity predicate, Resource @object) {
-			ResSet resources = new ResSet();
-			ResourceCollector collector = new ResourceCollector();
-			collector.SPO = 0;
-			collector.Table = resources;
-			Select(new Statement(null, predicate, @object, null), collector);
-			return resources.ToEntityArray();
-		}
-		class ResourceCollector : StatementSink {
-			public ResSet Table;
-			public int SPO;
-			public bool Add(Statement s) {
-				if (SPO == 0) Table.Add(s.Subject);
-				if (SPO == 2) Table.Add(s.Object);
-				return true;
-			}
-		}
-		class ResourceCollector2 : StatementSink {
-			System.Collections.ArrayList items = new System.Collections.ArrayList();
-			ResSet other = new ResSet();
-			public bool Add(Statement s) {
-				if (s.Predicate.Uri == null || !s.Predicate.Uri.StartsWith(NS.RDF + "_")) {
-					other.Add(s.Object);
-				} else {
-					string num = s.Predicate.Uri.Substring(NS.RDF.Length+1);
-					try {
-						int idx = int.Parse(num);
-						items.Add(new Item(s.Object, idx));
-					} catch {
-						other.Add(s.Object);
-					}
-				}
-				return true;
-			}
-			public Resource[] GetItems() {
-				items.Sort();
-				Resource[] ret = new Resource[items.Count + other.Count];
-				int ctr = 0;
-				foreach (Item item in items)
-					ret[ctr++] = item.r;
-				foreach (Resource item in other)
-					ret[ctr++] = item;
-				return ret;
-			}
-			class Item : IComparable {
-				public Resource r;
-				int idx;
-				public Item(Resource r, int idx) { this.r = r; this.idx = idx; }
-				public int CompareTo(object other) {
-					return idx.CompareTo(((Item)other).idx);
-				}
-			}
-		}
-		
-		// QueryableSource
-		
-		public SemWeb.Query.MetaQueryResult MetaQuery(Statement[] graph, SemWeb.Query.QueryOptions options) {
-			// If reasoning is applied, delegate this call to the last reasoner
-			// and pass it a clone of this store but with itself removed.
-			ReasoningHelper rh = GetReasoningHelper(null);
-			if (rh != null)
-				return rh.reasoner.MetaQuery(graph, options, rh.nextStore);
-			
-			// Special case for one wrapped data source that supports QueryableSource:
-			if (allsources.Count == 1 && allsources[0] is QueryableSource)
-				return ((QueryableSource)allsources[0]).MetaQuery(graph, options);
-		
-			return new SemWeb.Inference.SimpleEntailment().MetaQuery(graph, options, this);
-		}
-
-		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.
-			ReasoningHelper rh = GetReasoningHelper(null);
-			if (rh != null) {
-				rh.reasoner.Query(graph, options, rh.nextStore, sink);
-				return;
-			}
-
-			// Special case for one wrapped data source that supports QueryableSource:
-			if (allsources.Count == 1 && allsources[0] is QueryableSource) {
-				((QueryableSource)allsources[0]).Query(graph, options, sink);
-				return;
-			}
-
-			// Chunk the query graph as best we can.
-			SemWeb.Query.GraphMatch.QueryPart[] chunks = ChunkQuery(graph, options, sink);
-			
-			// If we couldn't chunk the graph, then just use the default GraphMatch implementation.
-			if (chunks == null) {
-				new SemWeb.Inference.SimpleEntailment().Query(graph, options, this, sink);
-				return;
-			}
-			
-			SemWeb.Query.GraphMatch.RunGeneralQuery(chunks, options.VariableKnownValues, options.VariableLiteralFilters, options.DistinguishedVariables,
-				0, options.Limit, true, sink);
-		}
-		
-		private SemWeb.Query.GraphMatch.QueryPart[] ChunkQuery(Statement[] query, SemWeb.Query.QueryOptions options, SemWeb.Query.QueryResultSink sink) {
-			// MetaQuery the data sources to get their capabilities.
-			SemWeb.Query.MetaQueryResult[] mq = new SemWeb.Query.MetaQueryResult[allsources.Count];
-			for (int i = 0; i < allsources.Count; i++) {
-				if (!(allsources[i] is QueryableSource))
-					return null;
-				mq[i] = ((QueryableSource)allsources[i]).MetaQuery(query, options);
-				if (!mq[i].QuerySupported)
-					return null;
-			}
-		
-			System.Collections.ArrayList chunks = new System.Collections.ArrayList();
-			
-			int curSource = -1;
-			System.Collections.ArrayList curStatements = new System.Collections.ArrayList();
-			
-			for (int j = 0; j < query.Length; j++) {
-				if (curSource != -1) {
-					// If we have a curSource and it definitively answers this
-					// 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;
-					}
-					
-					// If we have a curSource and no other source answers this
-					// statement, also include this statement in the current chunk.
-					bool foundOther = false;
-					for (int i = 0; i < mq.Length; i++) {
-						if (i == curSource) continue;
-						if (mq[i].NoData != null && mq[i].NoData[j]) continue;
-						foundOther = true;
-						break;
-					}
-					if (!foundOther) {
-						curStatements.Add(query[j]);
-						continue;
-					}
-					
-					// Some other source could possibly answer this statement,
-					// so we complete the chunk we started.
-					SemWeb.Query.GraphMatch.QueryPart c = new SemWeb.Query.GraphMatch.QueryPart(
-						(Statement[])curStatements.ToArray(typeof(Statement)),
-						(QueryableSource)allsources[curSource]
-						);
-					chunks.Add(c);
-					
-					curSource = -1;
-					curStatements.Clear();
-				}
-			
-				// Find a definitive source for this statement
-				for (int i = 0; i < mq.Length; i++) {
-					if (mq[i].IsDefinitive != null && mq[i].IsDefinitive[j]) {
-						curSource = i;
-						curStatements.Add(query[j]);
-						sink.AddComments(allsources[i] + " answers definitively: " + query[j]);
-						break;
-					}
-				}
-				if (curSource != -1) // found a definitive source
-					continue;
-					
-				// See if only one source can answer this statement.
-				// Also build a list of sources that can answer the
-				// statement, so don't break out of this loop early.
-				System.Collections.ArrayList answerables = new System.Collections.ArrayList();
-				int findSource = -1;
-				for (int i = 0; i < mq.Length; i++) {
-					if (mq[i].NoData != null && mq[i].NoData[j]) continue;
-					answerables.Add(allsources[i]);
-					if (findSource == -1)
-						findSource = i;
-					else
-						findSource = -2; // found a second source that can answer this
-				}
-				if (findSource >= 0) {
-					curSource = findSource;
-					curStatements.Add(query[j]);
-					continue;
-				}
-				if (answerables.Count == 0) {
-					sink.AddComments("No data source could answer: " + query[j]);
-					return null;
-				}
-				
-				// More than one source can answer this, so make a one-statement chunk.
-				SemWeb.Query.GraphMatch.QueryPart cc = new SemWeb.Query.GraphMatch.QueryPart(
-					query[j],
-					(QueryableSource[])answerables.ToArray(typeof(QueryableSource))
-					);
-				chunks.Add(cc);
-			}
-
-			if (curSource != -1) {
-				SemWeb.Query.GraphMatch.QueryPart c = new SemWeb.Query.GraphMatch.QueryPart(
-					(Statement[])curStatements.ToArray(typeof(Statement)),
-					(QueryableSource)allsources[curSource]
-					);
-				chunks.Add(c);
-			}
-			
-			return (SemWeb.Query.GraphMatch.QueryPart[])chunks.ToArray(typeof(SemWeb.Query.GraphMatch.QueryPart));
-		}
-
-		public
-		#if !DOTNET2
-		ICollection
-		#else
-		ICollection<SemWeb.Query.VariableBindings>
-		#endif
-		Query(Statement[] graph) {
-			SemWeb.Query.QueryOptions options = new SemWeb.Query.QueryOptions();
-			options.Limit = 1;
-			SemWeb.Query.QueryResultBuffer sink = new SemWeb.Query.QueryResultBuffer();
-			Query(graph, options, sink);
-			return sink.Bindings;
-		}
-		
-		// StaticSource
-		
-		public int StatementCount {
-			get {
-				int ret = 0;
-				foreach (StatementSource s in allsources) {
-					if (s is StaticSource)
-						ret += ((StaticSource)s).StatementCount;
-					else
-						throw new InvalidOperationException("Not all data sources are support StatementCount.");
-				}
-				return ret;
-			}
-		}
-		
-		public Entity[] GetEntities() {
-			ResSet h = new ResSet();
-			foreach (StatementSource s in allsources) {
-				if (s is StaticSource) {
-					foreach (Resource r in ((StaticSource)s).GetEntities())
-						h.Add(r);
-				} else {
-					throw new InvalidOperationException("Not all data sources support GetEntities.");
-				}
-			}
-			return h.ToEntityArray();
-		}
-		
-		public Entity[] GetPredicates() {
-			ResSet h = new ResSet();
-			foreach (StatementSource s in allsources) {
-				if (s is StaticSource) {
-					foreach (Resource r in ((StaticSource)s).GetPredicates())
-						h.Add(r);
-				} else {
-					throw new InvalidOperationException("Not data sources support GetPredicates.");
-				}
-			}
-			return h.ToEntityArray();
-		}
-
-		public Entity[] GetMetas() {
-			ResSet h = new ResSet();
-			foreach (StatementSource s in allsources) {
-				if (s is StaticSource) {
-					foreach (Resource r in ((StaticSource)s).GetMetas())
-						h.Add(r);
-				} else {
-					throw new InvalidOperationException("Not all data sources support GetMetas.");
-				}
-			}
-			return h.ToEntityArray();
-		}
-
-		public Entity[] GetEntitiesOfType(Entity type) {
-			return SelectSubjects(rdfType, type);
-		}
-		
-		public string GetPersistentBNodeId(BNode node) {
-			foreach (SelectableSource source in allsources) {
-				if (source is StaticSource) {
-					string id = ((StaticSource)source).GetPersistentBNodeId(node);
-					if (id != null) return id;
-				}
-			}
-			return null;
-		}
-		
-		public BNode GetBNodeFromPersistentId(string persistentId) {
-			foreach (SelectableSource source in allsources) {
-				if (source is StaticSource) {
-					BNode node = ((StaticSource)source).GetBNodeFromPersistentId(persistentId);
-					if (node != null) return node;
-				}
-			}
-			return null;
-		}
-		
-		
-		// StatementSink
-
-		bool StatementSink.Add(Statement statement) {
-			Add(statement);
-			return true;
-		}
-		
-		public void Add(Statement statement) {
-			if (statement.AnyNull) throw new ArgumentNullException();
-			// We don't know where to put it unless we are wrapping just one store.
-			SelectableSource[] sources = GetSources(ref statement.Meta);
-			if (sources == null || sources.Length != 1) throw new InvalidOperationException("I don't know which data source to put the statement into.");
-			if (!(sources[0] is ModifiableSource)) throw new InvalidOperationException("The data source is not modifiable.");
-			((ModifiableSource)sources[0]).Add(statement);
-		}
-		
-		// ModifiableSource
-		
-		public void Clear() {
-			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();
-		}
-		
-		ModifiableSource[] GetModifiableSources(ref Entity graph) {
-			SelectableSource[] sources = GetSources(ref graph);
-			if (sources == null) return null;
-			
-			// check all are modifiable first
-			foreach (SelectableSource source in sources)
-				if (!(source is ModifiableSource)) throw new InvalidOperationException("Not all of the data sources are modifiable.");
-				
-			ModifiableSource[] sources2 = new ModifiableSource[sources.Length];
-			sources.CopyTo(sources2, 0);
-			return sources2;
-		}
-
-		public void Remove(Statement template) {
-			ModifiableSource[] sources = GetModifiableSources(ref template.Meta);
-			if (sources == null) return;
-			
-			foreach (ModifiableSource source in sources)
-				source.Remove(template);
-		}
-
-		public void Import(StatementSource source) {
-			// We don't know where to put the data unless we are wrapping just one store.
-			if (allsources.Count != 1) throw new InvalidOperationException("I don't know which data source to put the statements into.");
-			if (!(allsources[0] is ModifiableSource)) throw new InvalidOperationException("The data source is not modifiable.");
-			((ModifiableSource)allsources[0]).Import(source);
-		}
-		
-		public void RemoveAll(Statement[] templates) {
-			// Not tested...
-			
-			System.Collections.ArrayList metas = new System.Collections.ArrayList();
-			foreach (Statement t in templates)
-				if (!metas.Contains(t.Meta))
-					metas.Add(t.Meta);
-					
-			foreach (Entity meta in metas) {
-				Entity meta2 = meta;
-				ModifiableSource[] sources = GetModifiableSources(ref meta2);
-				if (sources == null) continue;
-				
-				StatementList templates2 = new StatementList();
-				foreach (Statement t in templates) {
-					if (t.Meta == meta) {
-						Statement t2 = t;
-						t2.Meta = meta2;
-						templates2.Add(t2);
-					}
-				}
-					
-				foreach (ModifiableSource source in sources)
-					source.RemoveAll(templates2);
-			}
-		}
-		
-		public void Replace(Entity find, Entity replacement) {
-			foreach (SelectableSource source in allsources)
-				if (!(source is ModifiableSource)) throw new InvalidOperationException("Not all of the data sources are modifiable.");
-
-			foreach (ModifiableSource source in allsources)
-				source.Replace(find, replacement);
-		}
-		
-		public void Replace(Statement find, Statement replacement) {
-			ModifiableSource[] sources = GetModifiableSources(ref find.Meta);
-			if (sources == null) return;
-				
-			foreach (ModifiableSource source in sources)
-				source.Replace(find, replacement);
-		}
-
-		public static void DefaultReplace(ModifiableSource source, Entity find, Entity replacement) {
-			MemoryStore deletions = new MemoryStore();
-			MemoryStore additions = new MemoryStore();
-			
-			source.Select(new Statement(find, null, null, null), deletions);
-			source.Select(new Statement(null, find, null, null), deletions);
-			source.Select(new Statement(null, null, find, null), deletions);
-			source.Select(new Statement(null, null, null, find), deletions);
-			
-			foreach (Statement s in deletions) {
-				source.Remove(s);
-				additions.Add(s.Replace(find, replacement));
-			}
-			
-			foreach (Statement s in additions) {
-				source.Add(s);
-			}
-		}
-		
-		public static void DefaultReplace(ModifiableSource source, Statement find, Statement replacement) {
-			source.Remove(find);
-			source.Add(replacement);
-		}
-		
-		
-	}
-
-	public abstract class SelectResult : StatementSource, 
-#if DOTNET2
-	System.Collections.Generic.IEnumerable<Statement>
-#else
-	IEnumerable
-#endif
-	{
-		internal Store source;
-		MemoryStore ms;
-		internal SelectResult(Store source) { this.source = source; }
-		public bool Distinct { get { return source.Distinct; } }
-		public abstract void Select(StatementSink sink);
-#if DOTNET2
-		System.Collections.Generic.IEnumerator<Statement> System.Collections.Generic.IEnumerable<Statement>.GetEnumerator() {
-			return ((System.Collections.Generic.IEnumerable<Statement>)Buffer()).GetEnumerator();
-		}
-#endif
-		System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
-			return ((System.Collections.IEnumerable)Buffer()).GetEnumerator();
-		}
-		public long StatementCount { get { return Buffer().StatementCount; } }
-		public MemoryStore Load() { return Buffer(); }
-		public Statement[] ToArray() { return Load().ToArray(); }
-		private MemoryStore Buffer() {
-			if (ms != null) return ms;
-			ms = new MemoryStore();
-			ms.allowIndexing = false;
-			Select(ms);
-			return ms;
-		}
-		
-		internal class Single : SelectResult {
-			Statement template;
-			public Single(Store source, Statement template) : base(source) {
-				this.template = template;
-			}
-			public override void Select(StatementSink sink) {
-				source.Select(template, sink);
-			}
-		}
-		
-		internal class Multi : SelectResult {
-			SelectFilter filter;
-			public Multi(Store source, SelectFilter filter)
-				: base(source) {
-				this.filter = filter;
-			}
-			public override void Select(StatementSink sink) {
-				source.Select(filter, sink);
-			}
-		}
-	}
-}
-		
-
-
-
-/////// AUXILIARY STORE WRAPPERS /////////
-	
-namespace SemWeb.Stores {
-
-	#if DOTNET2
-	using System.Collections;
-	#endif
-
-	public abstract class SimpleSourceWrapper : SelectableSource {
-	
-		public virtual bool Distinct { get { return true; } }
-		
-		public virtual void Select(StatementSink sink) {
-			// The default implementation does not return
-			// anything for this call.
-		}
-		
-		public abstract bool Contains(Resource resource);
-
-		public virtual bool Contains(Statement template) {
-			template.Object = null; // reduce to another case (else there would be recursion)
-			return Store.DefaultContains(this, template);
-		}
-		
-		protected virtual void SelectAllSubject(Entity subject, StatementSink sink) {
-		}
-
-		protected virtual void SelectAllObject(Resource @object, StatementSink sink) {
-		}
-
-		protected virtual void SelectRelationsBetween(Entity subject, Resource @object, StatementSink sink) {
-		}
-		
-		protected virtual void SelectAllPairs(Entity predicate, StatementSink sink) {
-		}
-
-		protected virtual void SelectSubjects(Entity predicate, Resource @object, StatementSink sink) {
-		}
-
-		protected virtual void SelectObjects(Entity subject, Entity predicate, StatementSink sink) {
-		}
-
-		public void Select(Statement template, StatementSink sink) {
-			if (template.Meta != null && template.Meta != Statement.DefaultMeta) return;
-			if (template.Predicate != null && template.Predicate.Uri == null) return;
-			
-			if (template.Subject == null && template.Predicate == null && template.Object == null) {
-				Select(sink);
-			} else if (template.Subject != null && template.Predicate != null && template.Object != null) {
-				template.Meta = Statement.DefaultMeta;
-				if (Contains(template))
-					sink.Add(template);
-			} else if (template.Predicate == null) {
-				if (template.Subject == null)
-					SelectAllObject(template.Object, sink); 
-				else if (template.Object == null)
-					SelectAllSubject(template.Subject, sink); 
-				else
-					SelectRelationsBetween(template.Subject, template.Object, sink);
-			} else if (template.Subject != null && template.Object == null) {
-				SelectObjects(template.Subject, template.Predicate, sink);
-			} else if (template.Subject == null && template.Object != null) {
-				SelectSubjects(template.Predicate, template.Object, sink);
-			} else if (template.Subject == null && template.Object == null) {
-				SelectAllPairs(template.Predicate, sink);
-			}
-		}
-	
-		public void Select(SelectFilter filter, StatementSink sink) {
-			Store.DefaultSelect(this, filter, sink);
-		}
+
+namespace SemWeb {
+	
+	public interface StatementSource {
+		bool Distinct { get; }
+		void Select(StatementSink sink);
+	}
+	
+	public interface SelectableSource : StatementSource {
+		bool Contains(Statement template);
+		void Select(Statement template, StatementSink sink);
+		void Select(SelectFilter filter, StatementSink sink);
+	}
+
+	public interface QueryableSource : SelectableSource {
+		void Query(Statement[] graph, SemWeb.Query.QueryResultSink sink);
+		void Query(SelectFilter[] graph, SemWeb.Query.QueryResultSink sink);
+	}
+	
+	public interface StatementSink {
+		bool Add(Statement statement);
+	}
+
+	public interface ModifiableSource : StatementSink {
+		void Clear();
+		void Import(StatementSource source);
+		void Remove(Statement template);
+		void RemoveAll(Statement[] templates);
+		void Replace(Entity find, Entity replacement);
+		void Replace(Statement find, Statement replacement);
+	}
+	
+	internal class StatementCounterSink : StatementSink {
+		int counter = 0;
+		
+		public int StatementCount { get { return counter; } }
+		
+		public bool Add(Statement statement) {
+			counter++;
+			return true;
+		}
+	}
+
+	internal class StatementExistsSink : StatementSink {
+		bool exists = false;
+		
+		public bool Exists { get { return exists; } }
+		
+		public bool Add(Statement statement) {
+			exists = true;
+			return false;
+		}
+	}
+
+	public abstract class Store : StatementSource, StatementSink,
+		SelectableSource, ModifiableSource,
+		IDisposable {
+		
+		Entity rdfType;
+		
+		public static StatementSource CreateForInput(string spec) {
+			if (spec.StartsWith("rdfs+")) {
+				StatementSource s = CreateForInput(spec.Substring(5));
+				if (!(s is SelectableSource)) s = new MemoryStore(s);
+				return new SemWeb.Inference.RDFS(s, (SelectableSource)s);
+			}
+			return (StatementSource)Create(spec, false);
+		}		
+		
+		public static StatementSink CreateForOutput(string spec) {
+			return (StatementSink)Create(spec, true);
+		}
+		
+		private static object Create(string spec, bool output) {
+			string[] multispecs = spec.Split('\n', '|');
+			if (multispecs.Length > 1) {
+				SemWeb.Stores.MultiStore multistore = new SemWeb.Stores.MultiStore();
+				foreach (string mspec in multispecs) {
+					object mstore = Create(mspec.Trim(), output);
+					if (mstore is SelectableSource) {
+						multistore.Add((SelectableSource)mstore);
+					} else if (mstore is StatementSource) {
+						MemoryStore m = new MemoryStore((StatementSource)mstore);
+						multistore.Add(m);
+					}
+				}
+				return multistore;
+			}
+		
+			string type = spec;
+			
+			int c = spec.IndexOf(':');
+			if (c != -1) {
+				type = spec.Substring(0, c);
+				spec = spec.Substring(c+1);
+			} else {
+				spec = "";
+			}
+			
+			Type ttype;
+			
+			switch (type) {
+				case "mem":
+					return new MemoryStore();
+				case "xml":
+					if (spec == "") throw new ArgumentException("Use: xml:filename");
+					if (output) {
+						return new RdfXmlWriter(spec);
+					} else {
+						return new RdfXmlReader(spec);
+					}
+				case "n3":
+				case "ntriples":
+				case "nt":
+				case "turtle":
+					if (spec == "") throw new ArgumentException("Use: format:filename");
+					if (output) {
+						N3Writer ret = new N3Writer(spec); // turtle is default format
+						switch (type) {
+							case "nt": case "ntriples":
+								ret.Format = N3Writer.Formats.NTriples;
+								break;
+						}
+						return ret;
+					} else {
+						return new N3Reader(spec);
+					}
+				/*case "file":
+					if (spec == "") throw new ArgumentException("Use: format:filename");
+					if (output) throw new ArgumentException("The FileStore does not support writing.");
+					return new SemWeb.Stores.FileStore(spec);*/
+				case "sqlite":
+				case "mysql":
+				case "postgresql":
+					if (spec == "") throw new ArgumentException("Use: sqlite|mysql|postgresql:table:connection-string");
+				
+					c = spec.IndexOf(':');
+					if (c == -1) throw new ArgumentException("Invalid format for SQL spec parameter (table:constring).");
+					string table = spec.Substring(0, c);
+					spec = spec.Substring(c+1);
+					
+					string classtype = null;
+					if (type == "sqlite") {
+						classtype = "SemWeb.Stores.SqliteStore, SemWeb.SqliteStore";
+						spec = spec.Replace(";", ",");
+					} else if (type == "mysql") {
+						classtype = "SemWeb.Stores.MySQLStore, SemWeb.MySQLStore";
+					} else if (type == "postgresql") {
+						classtype = "SemWeb.Stores.PostgreSQLStore, SemWeb.PostgreSQLStore";
+					}
+					ttype = Type.GetType(classtype);
+					if (ttype == null)
+						throw new NotSupportedException("The storage type in <" + classtype + "> could not be found.");
+					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 "class":
+					ttype = Type.GetType(spec);
+					if (ttype == null)
+						throw new NotSupportedException("The class <" + spec + "> could not be found.");
+					return Activator.CreateInstance(ttype);
+				default:
+					throw new ArgumentException("Unknown parser type: " + type);
+			}
+		}
+		
+		protected Store() {
+			rdfType = new Entity("http://www.w3.org/1999/02/22-rdf-syntax-ns#type";);
+		}
+		
+		void IDisposable.Dispose() {
+			Close();
+		}
+		
+		public virtual void Close() {
+		}
+		
+		public abstract bool Distinct { get; }
+		
+		public abstract int StatementCount { get; }
+
+		public abstract void Clear();
+
+		public Entity[] GetEntitiesOfType(Entity type) {
+			ArrayList entities = new ArrayList();
+			
+			IEnumerable result = Select(new Statement(null, rdfType, type));
+			foreach (Statement s in result) {
+				entities.Add(s.Subject);
+			}
+			
+			return (Entity[])entities.ToArray(typeof(Entity));
+		}
+		
+		bool StatementSink.Add(Statement statement) {
+			Add(statement);
+			return true;
+		}
+		
+		public abstract void Add(Statement statement);
+		
+		public abstract void Remove(Statement statement);
+
+		public virtual void Import(StatementSource source) {
+			source.Select(this);
+		}
+		
+		public void RemoveAll(Statement[] templates) {
+			foreach (Statement t in templates)
+				Remove(t);
+		}
+		
+		public abstract Entity[] GetEntities();
+		
+		public abstract Entity[] GetPredicates();
+		
+		public abstract Entity[] GetMetas();
+		
+		public virtual bool Contains(Statement template) {
+			return DefaultContains(this, template);
+		}
+
+		public static bool DefaultContains(SelectableSource source, Statement template) {
+			StatementExistsSink sink = new StatementExistsSink();
+			SelectFilter filter = new SelectFilter(template);
+			filter.Limit = 1;
+			source.Select(filter, sink);
+			return sink.Exists;
+		}
+		
+		public static void DefaultSelect(SelectableSource source, SelectFilter filter, StatementSink sink) {
+			// This method should be avoided...
+			if (filter.LiteralFilters != null)
+				sink = new SemWeb.Filters.FilterSink(filter.LiteralFilters, sink, source);
+			foreach (Entity subject in filter.Subjects == null ? new Entity[] { null } : filter.Subjects)
+			foreach (Entity predicate in filter.Predicates == null ? new Entity[] { null } : filter.Predicates)
+			foreach (Resource objct in filter.Objects == null ? new Resource[] { null } : filter.Objects)
+			foreach (Entity meta in filter.Metas == null ? new Entity[] { null } : filter.Metas)
+				source.Select(new Statement(subject, predicate, objct, meta), sink);
+		}		
+		
+		public void Select(StatementSink result) {
+			Select(Statement.All, result);
+		}
+		
+		public abstract void Select(Statement template, StatementSink result);
+		
+		public abstract void Select(SelectFilter filter, StatementSink result);
+		
+		public SelectResult Select(Statement template) {
+			return new SelectResult.Single(this, template);
+		}
+		
+		public SelectResult Select(SelectFilter filter) {
+			return new SelectResult.Multi(this, filter);
+		}
+		
+		public Resource[] SelectObjects(Entity subject, Entity predicate) {
+			Hashtable resources = new Hashtable();
+			ResourceCollector collector = new ResourceCollector();
+			collector.SPO = 2;
+			collector.Table = resources;
+			Select(new Statement(subject, predicate, null, null), collector);
+			return (Resource[])new ArrayList(resources.Keys).ToArray(typeof(Resource));
+		}
+		public Entity[] SelectSubjects(Entity predicate, Resource @object) {
+			Hashtable resources = new Hashtable();
+			ResourceCollector collector = new ResourceCollector();
+			collector.SPO = 0;
+			collector.Table = resources;
+			Select(new Statement(null, predicate, @object, null), collector);
+			return (Entity[])new ArrayList(resources.Keys).ToArray(typeof(Entity));
+		}
+		class ResourceCollector : StatementSink {
+			public Hashtable Table;
+			public int SPO;
+			public bool Add(Statement s) {
+				if (SPO == 0) Table[s.Subject] = Table;
+				if (SPO == 2) Table[s.Object] = Table;
+				return true;
+			}
+		}
+		
+		public virtual void Replace(Entity find, Entity replacement) {
+			MemoryStore deletions = new MemoryStore();
+			MemoryStore additions = new MemoryStore();
+			
+			Select(new Statement(find, null, null, null), deletions);
+			Select(new Statement(null, find, null, null), deletions);
+			Select(new Statement(null, null, find, null), deletions);
+			Select(new Statement(null, null, null, find), deletions);
+			
+			foreach (Statement s in deletions) {
+				Remove(s);
+				additions.Add(s.Replace(find, replacement));
+			}
+			
+			foreach (Statement s in additions) {
+				Add(s);
+			}
+		}
+		
+		public virtual void Replace(Statement find, Statement replacement) {
+			Remove(find);
+			Add(replacement);
+		}
+		
		public void Write(System.IO.TextWriter writer) {
+			using (RdfWriter w = new N3Writer(writer)) {
+				Select(w);
+			}
+		}
+		
+		protected object GetResourceKey(Resource resource) {
+			return resource.GetResourceKey(this);
+		}
+
+		protected void SetResourceKey(Resource resource, object value) {
+			resource.SetResourceKey(this, value);
+		}
+		
+	}
+
+	public abstract class SelectResult : StatementSource, IEnumerable {
+		internal Store source;
+		MemoryStore ms;
+		internal SelectResult(Store source) { this.source = source; }
+		public bool Distinct { get { return source.Distinct; } }
+		public abstract void Select(StatementSink sink);
+		public IEnumerator GetEnumerator() {
+			return Buffer().Statements.GetEnumerator();
+		}
+		public long StatementCount { get { return Buffer().StatementCount; } }
+		public MemoryStore Load() { return Buffer(); }
+		public Statement[] ToArray() { return Load().ToArray(); }
+		private MemoryStore Buffer() {
+			if (ms != null) return ms;
+			ms = new MemoryStore();
+			ms.allowIndexing = false;
+			Select(ms);
+			return ms;
+		}
+		
+		internal class Single : SelectResult {
+			Statement template;
+			public Single(Store source, Statement template) : base(source) {
+				this.template = template;
+			}
+			public override void Select(StatementSink sink) {
+				source.Select(template, sink);
+			}
+		}
+		
+		internal class Multi : SelectResult {
+			SelectFilter filter;
+			public Multi(Store source, SelectFilter filter)
+				: base(source) {
+				this.filter = filter;
+			}
+			public override void Select(StatementSink sink) {
+				source.Select(filter, sink);
+			}
+		}
+	}
+}
+
+namespace SemWeb.Stores {
+
+	public interface SupportsPersistableBNodes {
+		string GetStoreGuid();
+		string GetNodeId(BNode node);
+		BNode GetNodeFromId(string persistentId);
+	}
+
+	public class MultiStore : Store {
+		ArrayList stores = new ArrayList();
+		Hashtable namedgraphs = new Hashtable();
+		ArrayList allsources = new ArrayList();
+		
+		public MultiStore() { }
+		
+		public override bool Distinct { get { return false; } }
+		
+		public void Add(SelectableSource store) {
+			stores.Add(store);
+			allsources.Add(store);
+		}
+		
+		public void Add(string uri, SelectableSource store) {
+			namedgraphs[uri] = store;
+			allsources.Add(store);
+		}
+		
+		public void Add(RdfReader source) {
+			MemoryStore store = new MemoryStore(source);
+			Add(store);
+		}
+		
+		public void Add(string uri, RdfReader source) {
+			MemoryStore store = new MemoryStore(source);
+			Add(uri, store);
+		}
+		
+		public void Remove(SelectableSource store) {
+			stores.Remove(store);
+			allsources.Remove(store);
+		}
+		
+		public void Remove(string uri) {
+			allsources.Remove(namedgraphs[uri]);
+			namedgraphs.Remove(uri);
+		}
+		
+		public override int StatementCount {
+			get {
+				int ret = 0;
+				foreach (StatementSource s in allsources) {
+					if (s is Store)
+						ret += ((Store)s).StatementCount;
+					else
+						throw new InvalidOperationException("Not all sources are stores supporting StatementCount.");
+				}
+				return ret;
+			}
+		}
+
+		public override void Clear() {
+			throw new InvalidOperationException("Clear is not a valid operation on a MultiStore.");
+		}
+		
+		public override Entity[] GetEntities() {
+			Hashtable h = new Hashtable();
+			foreach (StatementSource s in allsources) {
+				if (s is Store) {
+					foreach (Resource r in ((Store)s).GetEntities())
+						h[r] = h;
+				} else {
+					throw new InvalidOperationException("Not all sources are stores supporting GetEntities.");
+				}
+			}
+			return (Entity[])new ArrayList(h.Keys).ToArray(typeof(Entity));
+		}
+		
+		public override Entity[] GetPredicates() {
+			Hashtable h = new Hashtable();
+			foreach (StatementSource s in allsources) {
+				if (s is Store) {
+					foreach (Resource r in ((Store)s).GetPredicates())
+						h[r] = h;
+				} else {
+					throw new InvalidOperationException("Not all sources are stores supporting GetEntities.");
+				}
+			}
+			return (Entity[])new ArrayList(h.Keys).ToArray(typeof(Entity));
+		}
+
+		public override Entity[] GetMetas() {
+			Hashtable h = new Hashtable();
+			foreach (StatementSource s in allsources) {
+				if (s is Store) {
+					foreach (Resource r in ((Store)s).GetMetas())
+						h[r] = h;
+				} else {
+					throw new InvalidOperationException("Not all sources are stores supporting GetEntities.");
+				}
+			}
+			return (Entity[])new ArrayList(h.Keys).ToArray(typeof(Entity));
+		}
+
+		public override void Add(Statement statement) { throw new InvalidOperationException("Add is not a valid operation on a MultiStore."); }
+		
+		SelectableSource[] GetSources(Entity graph) {
+			if (graph == null || namedgraphs.Count == 0)
+				return (SelectableSource[])allsources.ToArray(typeof(SelectableSource));
+			else if (graph == Statement.DefaultMeta)
+				return (SelectableSource[])stores.ToArray(typeof(SelectableSource));
+			else if (graph.Uri != null && namedgraphs.ContainsKey(graph.Uri))
+				return new SelectableSource[] { (SelectableSource)namedgraphs[graph.Uri] };
+			else
+				return null;
+		}
+		
+		public override bool Contains(Statement statement) {
+			SelectableSource[] sources = GetSources(statement.Meta);
+			if (sources == null) return false;
+			foreach (SelectableSource s in sources)
+				if (s.Contains(statement))
+					return true;
+			return false;
+		}
+			
+		public override void Remove(Statement statement) { throw new InvalidOperationException("Remove is not a valid operation on a MultiStore."); }
+		
+		public override void Select(Statement template, StatementSink result) {
+			SelectableSource[] sources = GetSources(template.Meta);
+			if (sources == null) return;
+			template.Meta = null;
+			foreach (SelectableSource s in sources)
+				s.Select(template, result);
+		}
+		
+		public override void Select(SelectFilter filter, StatementSink result) {
+			Entity[] scanMetas = filter.Metas;
+			filter.Metas = null;
+			if (scanMetas == null || namedgraphs.Count == 0) scanMetas = new Entity[] { null };
+			foreach (Entity meta in scanMetas) {
+				SelectableSource[] sources = GetSources(meta);
+				if (sources == null) continue;
+				foreach (SelectableSource s in sources)
+					s.Select(filter, result);
+			}
+		}
+
+		public override void Replace(Entity a, Entity b) { throw new InvalidOperationException("Replace is not a valid operation on a MultiStore."); }
+		
+		public override void Replace(Statement find, Statement replacement) { throw new InvalidOperationException("Replace is not a valid operation on a MultiStore."); }
+		
+	}
+	
+	public abstract class SimpleSourceWrapper : SelectableSource {
+	
+		public virtual bool Distinct { get { return true; } }
+		
+		public virtual void Select(StatementSink sink) {
+			// The default implementation does not return
+			// anything for this call.
+		}
+
+		public virtual bool Contains(Statement template) {
+			template.Object = null; // reduce to another case (else there would be recursion)
+			return Store.DefaultContains(this, template);
+		}
+		
+		protected virtual void SelectAllSubject(Entity subject, StatementSink sink) {
+		}
+
+		protected virtual void SelectAllObject(Resource @object, StatementSink sink) {
+		}
+
+		protected virtual void SelectRelationsBetween(Entity subject, Resource @object, StatementSink sink) {
+		}
+		
+		protected virtual void SelectAllPairs(Entity predicate, StatementSink sink) {
+		}
+
+		protected virtual void SelectSubjects(Entity predicate, Resource @object, StatementSink sink) {
+		}
+
+		protected virtual void SelectObjects(Entity subject, Entity predicate, StatementSink sink) {
+		}
+
+		public void Select(Statement template, StatementSink sink) {
+			if (template.Meta != null && template.Meta != Statement.DefaultMeta) return;
+			if (template.Predicate != null && template.Predicate.Uri == null) return;
+			
+			if (template.Subject == null && template.Predicate == null && template.Object == null) {
+				Select(sink);
+			} else if (template.Subject != null && template.Predicate != null && template.Object != null) {
+				template.Meta = Statement.DefaultMeta;
+				if (Contains(template))
+					sink.Add(template);
+			} else if (template.Predicate == null) {
+				if (template.Subject == null)
+					SelectAllObject(template.Object, sink); 
+				else if (template.Object == null)
+					SelectAllSubject(template.Subject, sink); 
+				else
+					SelectRelationsBetween(template.Subject, template.Object, sink);
+			} else if (template.Subject != null && template.Object == null) {
+				SelectObjects(template.Subject, template.Predicate, sink);
+			} else if (template.Subject == null && template.Object != null) {
+				SelectSubjects(template.Predicate, template.Object, sink);
+			} else if (template.Subject == null && template.Object == null) {
+				SelectAllPairs(template.Predicate, sink);
+			}
+		}
+	
+		public void Select(SelectFilter filter, StatementSink sink) {
+			Store.DefaultSelect(this, filter, sink);
+		}
 	}
 	
-	public class DebuggedSource : QueryableSource {
+	public class DebuggedSource : SelectableSource {
 		SelectableSource source;
 		System.IO.TextWriter output;
 		
@@ -981,97 +595,50 @@
 			this.output = output;
 		}
 		
-		public bool Distinct { get { return source.Distinct; } }
+		public bool Distinct { get { return source.Distinct; } }
 		
 		public void Select(StatementSink sink) {
 			Select(Statement.All, sink);
-		}
-		
-		public bool Contains(Resource resource) {
-			output.WriteLine("CONTAINS: " + resource);
-			return source.Contains(resource);
-		}
+		}
 
 		public bool Contains(Statement template) {
 			output.WriteLine("CONTAINS: " + template);
 			return source.Contains(template);
 		}
-		
+		
 		public void Select(Statement template, StatementSink sink) {
 			output.WriteLine("SELECT: " + template);
 			source.Select(template, sink);
 		}
-	
+	
 		public void Select(SelectFilter filter, StatementSink sink) {
 			output.WriteLine("SELECT: " + filter);
 			source.Select(filter, sink);
-		}
-
-		public virtual SemWeb.Query.MetaQueryResult MetaQuery(Statement[] graph, SemWeb.Query.QueryOptions options) {
-			if (source is QueryableSource)
-				return ((QueryableSource)source).MetaQuery(graph, options);
-			else
-				return new SemWeb.Query.MetaQueryResult(); // QuerySupported is by default false
-		}
-
-		public void Query(Statement[] graph, SemWeb.Query.QueryOptions options, SemWeb.Query.QueryResultSink sink) {
-			output.WriteLine("QUERY:");
-			foreach (Statement s in graph)
-				output.WriteLine("\t" + s);
-			if (options.VariableKnownValues != null) {
-				#if !DOTNET2
-				foreach (System.Collections.DictionaryEntry ent in options.VariableKnownValues)
-				#else
-				foreach (KeyValuePair<Variable,ICollection<Resource>> ent in options.VariableKnownValues)
-				#endif
-					output.WriteLine("\twhere " + ent.Key + " in " + ToString((ICollection)ent.Value));	
-			}
-			if (source is QueryableSource) 
-				((QueryableSource)source).Query(graph, options, sink);
-			else
-				throw new NotSupportedException("Underlying source " + source + " is not a QueryableSource.");
-		}
-		
-		string ToString(ICollection resources) {
-			ArrayList s = new ArrayList();
-			foreach (Resource r in resources)
-				s.Add(r.ToString());
-			return String.Join(",", (string[])s.ToArray(typeof(string)));
-		}
+		}
 	}
 	
 	public class CachedSource : SelectableSource {
 		SelectableSource source;
 
-		Hashtable containsresource = new Hashtable();
-		StatementMap containsstmtresults = new StatementMap();
+		StatementMap containsresults = new StatementMap();
 		StatementMap selectresults = new StatementMap();
 		Hashtable selfilterresults = new Hashtable();
 	
 		public CachedSource(SelectableSource s) { source = s; }
 	
-		public bool Distinct { get { return source.Distinct; } }
+		public bool Distinct { get { return source.Distinct; } }
 		
 		public void Select(StatementSink sink) {
 			Select(Statement.All, sink);
-		}
-
-		public bool Contains(Resource resource) {
-			if (source == null) return false;
-			if (!containsresource.ContainsKey(resource))
-				containsresource[resource] = source.Contains(resource);
-			return (bool)containsresource[resource];
-		}
+		}
 
 		public bool Contains(Statement template) {
-			if (source == null) return false;
-			if (!containsstmtresults.ContainsKey(template))
-				containsstmtresults[template] = source.Contains(template);
-			return (bool)containsstmtresults[template];
+			if (!containsresults.ContainsKey(template))
+				containsresults[template] = source.Contains(template);
+			return (bool)containsresults[template];
 		}
-		
+		
 		public void Select(Statement template, StatementSink sink) {
-			if (source == null) return;
 			if (!selectresults.ContainsKey(template)) {
 				MemoryStore s = new MemoryStore();
 				source.Select(template, s);
@@ -1079,95 +646,15 @@
 			}
 			((MemoryStore)selectresults[template]).Select(sink);
 		}
-	
+	
 		public void Select(SelectFilter filter, StatementSink sink) {
-			if (source == null) return;
 			if (!selfilterresults.ContainsKey(filter)) {
 				MemoryStore s = new MemoryStore();
 				source.Select(filter, s);
 				selfilterresults[filter] = s;
 			}
 			((MemoryStore)selfilterresults[filter]).Select(sink);
-		}
-
-	}
-	
-	internal class DecoupledStatementSource : StatementSource {
-		StatementSource source;
-		int minbuffersize = 2000;
-		int maxbuffersize = 10000;
-		
-		bool bufferWanted = false;
-		System.Threading.AutoResetEvent bufferMayAcquire = new System.Threading.AutoResetEvent(false);
-		System.Threading.AutoResetEvent bufferReleased = new System.Threading.AutoResetEvent(false);
-
-		System.Threading.Thread sourceThread;
-		
-		StatementList buffer = new StatementList();
-		bool sourceFinished = false;
-		
-		public DecoupledStatementSource(StatementSource source) {
-			this.source = source;
-		}
-		
-		public bool Distinct { get { return source.Distinct; } }
+		}
 
-		public void Select(StatementSink sink) {
-			bufferWanted = false;
-			
-			sourceThread = new System.Threading.Thread(Go);
-			sourceThread.Start();
-			
-			while (true) {
-				bufferWanted = true;
-				if (!sourceFinished) bufferMayAcquire.WaitOne(); // wait until we can have the buffer
-				bufferWanted = false;
-				
-				Statement[] statements = buffer.ToArray();
-				buffer.Clear();
-				
-				bufferReleased.Set(); // notify that we don't need the buffer anymore
-
-				if (sourceFinished && statements.Length == 0) break;
-				
-				foreach (Statement s in statements)
-					sink.Add(s);
-			}
-		}
-		
-		private void Go() {
-			source.Select(new MySink(this));
-			sourceFinished = true;
-			bufferMayAcquire.Set(); // for the last batch
-		}
-		
-		private void SourceAdd(Statement s) {
-			if ((bufferWanted && buffer.Count > minbuffersize) || buffer.Count >= maxbuffersize) {
-				bufferMayAcquire.Set();
-				bufferReleased.WaitOne();
-			}
-			buffer.Add(s);
-		}
-		private void SourceAdd(Statement[] s) {
-			if ((bufferWanted && buffer.Count > minbuffersize) || buffer.Count >= maxbuffersize) {
-				bufferMayAcquire.Set();
-				bufferReleased.WaitOne();
-			}
-			foreach (Statement ss in s)
-				buffer.Add(ss);
-		}
-		
-		private class MySink : StatementSink {
-			DecoupledStatementSource x;
-			public MySink(DecoupledStatementSource x) { this.x = x; }
-			public bool Add(Statement s) {
-				x.SourceAdd(s);
-				return true;
-			}
-			public bool Add(Statement[] s) {
-				x.SourceAdd(s);
-				return true;
-			}
-		}
-	}
-}
+	}
+}

Modified: trunk/semweb/Util.cs
==============================================================================
--- trunk/semweb/Util.cs	(original)
+++ trunk/semweb/Util.cs	Fri May 16 18:52:24 2008
@@ -11,24 +11,11 @@
 		
 		public ResSet() {
 		}
-		
-		#if !DOTNET2
-		public ResSet(ICollection items) {
-		#else
-		public ResSet(System.Collections.Generic.ICollection<Resource> items) {
-		#endif
+		
+		public ResSet(ICollection items) {
 			AddRange(items);
 		}
 
-		#if DOTNET2
-		// this is for some call in SQLStore; it seems to having a generics casting issue that I don't know if it's a mono bug or what...
-		internal ResSet(System.Collections.Generic.ICollection<Variable> items) {
-			if (items == null) return;
-			foreach (Resource r in items)
-				Add(r);
-		}
-		#endif
-
 		private ResSet(Hashtable items) {
 			this.items = items;
 		}
@@ -37,12 +24,8 @@
 			items[res] = items;
 			keys = null;
 		}
-		
-		#if !DOTNET2
-		public void AddRange(ICollection items) {
-		#else
-		public void AddRange<T>(System.Collections.Generic.ICollection<T> items) where T : Resource {
-		#endif
+		
+		public void AddRange(ICollection items) {
 			if (items == null) return;
 			foreach (Resource r in items)
 				Add(r);

Added: trunk/semweb/XPathSemWebNavigator.cs
==============================================================================
--- (empty file)
+++ trunk/semweb/XPathSemWebNavigator.cs	Fri May 16 18:52:24 2008
@@ -0,0 +1,353 @@
+using System;
+using System.Collections;
+using System.Xml;
+using System.Xml.Xsl;
+using System.Xml.XPath;
+
+using SemWeb;
+
+namespace SemWeb.Util {
+	public class XPathSemWebNavigator : XPathNavigator {
+		Store model;
+		NamespaceManager nsmgr;
+		NSWrap nswrap;
+		
+		ArrayList stack = new ArrayList();
+		Position current;
+		
+		private static Entity ExpandEntitiesOfType = new BNode();
+		
+		Entity rdfType = NS.RDF+"type";
+		
+		private class Position {
+			public int Index;
+			public bool FirstChild, LastChild;
+			
+			public Entity Predicate;
+			public Resource Object;
+			
+			public Position[] Children;
+		}
+		
+		private class NSWrap : XsltContext {
+			NamespaceManager nsmgr;
+			
+			public NSWrap(NamespaceManager nsmgr) : base(new NameTable()) { this.nsmgr = nsmgr; }
+			
+			public override bool HasNamespace (string prefix) {
+				return LookupNamespace(prefix) != null;
+			}
+			
+			public override string LookupNamespace (string prefix) {
+				return nsmgr.GetNamespace(prefix);
+			}
+			public override string LookupPrefix (string uri) {
+				return nsmgr.GetPrefix(uri);
+			}
+			
+			// These don't really do anything (yet?).
+			
+			public override bool Whitespace {
+				get { return false; }
+			} 
+			
+			public override int CompareDocument (string baseUri, string nextbaseUri) {
+				return baseUri.CompareTo(nextbaseUri);
+			}
+			
+			public override bool PreserveWhitespace (System.Xml.XPath.XPathNavigator node) {
+				return false;
+			}
+			
+			public override IXsltContextFunction ResolveFunction (string prefix, string name, System.Xml.XPath.XPathResultType[] ArgTypes) {
+				return null;
+			}
+
+			public override IXsltContextVariable ResolveVariable (string prefix, string name) {
+				return null;
+			}
+		}
+
+		public XPathSemWebNavigator(Store model, NamespaceManager namespaces) : this(ExpandEntitiesOfType, model, namespaces, null) { }
+
+		public XPathSemWebNavigator(Entity root, Store model, NamespaceManager namespaces) : this(root, model, namespaces, null) { }
+		
+		private XPathSemWebNavigator(Entity root, Store model, NamespaceManager namespaces, string exapandThisPredicate) {
+			this.model = model;
+			
+			if (!(namespaces is SemWeb.IO.AutoPrefixNamespaceManager))
+				namespaces = new SemWeb.IO.AutoPrefixNamespaceManager(namespaces);
+			this.nsmgr = namespaces;
+			nswrap = new NSWrap(nsmgr);
+			
+			Position start = new Position();
+			start.FirstChild = true;
+			start.LastChild = true;
+			start.Predicate = root; // a trick to make sure the URI info for the root reflects the root
+			start.Object = root;
+			if (exapandThisPredicate != null)
+				Expand(start, exapandThisPredicate);
+			current = start;
+		}
+		
+		private XPathSemWebNavigator(XPathSemWebNavigator clone) {
+			MoveTo(clone);
+		}
+		
+		private void Expand(Position p) {
+			Expand(p, null);
+		}
+		
+		private void Expand(Position p, string expandOnlyThisPredicate) {
+			if (!(p.Object is Entity)) {
+				p.Children = new Position[0];
+				return;
+			}
+			
+			ArrayList children = new ArrayList();
+			int ctr = 0;
+			
+			if (p.Object == ExpandEntitiesOfType) {
+				if (expandOnlyThisPredicate == null) {
+					// Get a list of entities and their types.
+					foreach (Statement s in model.Select(new Statement(null, rdfType, null))) {
+						if (!(s.Object is Entity)) continue;
+						Position c = new Position();
+						c.Index = ctr++;
+						c.Predicate = (Entity)s.Object;
+						c.Object = s.Subject;
+						children.Add(c);
+					}
+				} else {
+					foreach (Entity e in model.GetEntitiesOfType(expandOnlyThisPredicate)) {
+						Position c = new Position();
+						c.Predicate = expandOnlyThisPredicate;
+						c.Index = ctr++;
+						c.Object = e;
+						children.Add(c);
+					}
+				}
+			} else {
+
+			if (expandOnlyThisPredicate == null || !expandOnlyThisPredicate.StartsWith("!")) {
+				Statement q = new Statement(
+					(Entity)p.Object,
+					expandOnlyThisPredicate == null ? (Entity)null : (Entity)expandOnlyThisPredicate,
+					null);
+				
+				foreach (Statement s in model.Select(q)) {
+					Position c = new Position();
+					c.Index = ctr++;
+					c.Predicate = s.Predicate;
+					c.Object = s.Object;
+					children.Add(c);
+				}
+			}
+			
+			if (expandOnlyThisPredicate == null || expandOnlyThisPredicate.StartsWith("!")) {
+				Statement q = new Statement(
+					null,
+					expandOnlyThisPredicate == null ? (Entity)null : (Entity)expandOnlyThisPredicate.Substring(1),
+					p.Object);
+				
+				foreach (Statement s in model.Select(q)) {
+					Position c = new Position();
+					c.Index = ctr++;
+					c.Predicate = "!" + s.Predicate;
+					c.Object = s.Subject;
+					children.Add(c);
+				}
+			}
+			
+			}
+			
+			p.Children = (Position[])children.ToArray(typeof(Position));
+			
+			if (p.Children.Length > 0) {
+				p.Children[0].FirstChild = true;
+				p.Children[p.Children.Length-1].LastChild = true;
+			}
+		}
+
+		public override string BaseURI { get { return ""; } }
+
+		public override bool HasAttributes { get { return false; } }
+
+		public override bool HasChildren { get { return true; } }
+
+		public override bool IsEmptyElement { get { return false; } }
+
+		public override string LocalName {
+			get {
+				string p, l;
+				if (current.Predicate == ExpandEntitiesOfType)
+					return "root";
+				if (current.Predicate.Uri == null)
+					return "anonymous";
+				if (nsmgr.Normalize(current.Predicate.Uri, out p, out l))
+					return l;
+				throw new InvalidOperationException("The local name of " + current.Predicate.Uri + " could not be determined.");
+			}
+		}
+
+		public override string Name {
+			get {
+				if (current.Predicate == ExpandEntitiesOfType)
+					return "root";
+				if (current.Predicate.Uri == null)
+					return "anonymous";
+				return nsmgr.Normalize(current.Predicate.Uri);
+			}
+		}
+
+		public override string NamespaceURI {
+			get {
+				string p, l;
+				if (current.Predicate.Uri == null)
+					return "anonymous";
+				if (nsmgr.Normalize(current.Predicate.Uri, out p, out l))
+					return nsmgr.GetNamespace(p);
+				throw new InvalidOperationException("The namespace URI of " + current.Predicate.Uri + " could not be determined.");
+			}
+		}
+
+		public override XmlNameTable NameTable {
+			get {
+				return null;
+			}
+		}
+
+		public override XPathNodeType NodeType {
+			get {
+				if (stack.Count == 0)
+					return XPathNodeType.Root;
+				return XPathNodeType.Element;
+			}
+		}
+
+		public override string Prefix {
+			get {
+				string p, l;
+				if (nsmgr.Normalize(current.Predicate.Uri, out p, out l))
+					return p;
+				throw new InvalidOperationException("The prefix of " + current.Predicate.Uri + " could not be determined.");
+			}
+		}
+
+		public override string Value {
+			get {
+				if (current.Predicate == ExpandEntitiesOfType)
+					return "root";
+				if (current.Object is Literal)
+					return ((Literal)current.Object).Value;
+				if (current.Object.Uri == null)
+					return "";
+				return current.Object.Uri;
+			}
+		}
+
+		public override string XmlLang { get { return ""; } }
+
+		public override XPathNavigator Clone () {
+			return new XPathSemWebNavigator(this);
+		}
+
+		//public virtual XmlNodeOrder ComparePosition (XPathNavigator nav)
+		
+		public override string GetAttribute (string localName, string namespaceURI) {
+			return "";
+		}
+
+		public override string GetNamespace (string name) {
+			return nsmgr.GetNamespace(name);
+		}
+		
+		public override bool IsSamePosition (XPathNavigator other) {
+			if (!(other is XPathSemWebNavigator)) return false;
+			XPathSemWebNavigator o = (XPathSemWebNavigator)other;
+			return (o.current == current);
+		}
+
+		public override bool MoveTo (XPathNavigator other) {
+			XPathSemWebNavigator clone = other as XPathSemWebNavigator;
+			if (clone == null) return false;
+			this.model = clone.model;
+			this.nsmgr = clone.nsmgr;
+			this.nswrap = clone.nswrap;
+			this.stack = (ArrayList)clone.stack.Clone();
+			this.current = clone.current;
+			return true;
+		}
+
+		public override bool MoveToAttribute (string localName, string namespaceURI) { return false; }
+
+		public override bool MoveToNamespace (string name) { return false; }
+
+		public override bool MoveToFirst () {
+			return MoveToFirstChild();
+		}
+
+		public override void MoveToRoot () {
+			if (stack.Count == 0) return;
+			current = (Position)stack[0];
+			stack.Clear();
+		}
+
+		public override bool MoveToFirstAttribute () { return false; }
+
+		public override bool MoveToFirstChild () {
+			if (current.Children == null) Expand(current);
+			if (current.Children.Length == 0) return false;
+			stack.Add(current);
+			current = current.Children[0];
+			return true;
+		}
+
+		public override bool MoveToFirstNamespace (XPathNamespaceScope namespaceScope) { return false; }
+
+		public override bool MoveToId (string id) { return false; }
+
+		public override bool MoveToNext () {
+			if (current.LastChild) return false;
+			current = ((Position)stack[stack.Count-1]).Children[current.Index+1];
+			return true;
+		}
+
+		public override bool MoveToNextAttribute () { return false; }
+
+		public override bool MoveToNextNamespace (XPathNamespaceScope namespaceScope) { return false; }
+
+		public override bool MoveToParent () {
+			if (stack.Count == 0) return false;
+			current = (Position)stack[stack.Count-1];
+			stack.RemoveAt(stack.Count-1);
+			return true;
+		}
+
+		public override bool MoveToPrevious () {
+			if (current.FirstChild) return false;
+			current = ((Position)stack[stack.Count-1]).Children[current.Index-1];
+			return true;
+		}
+		
+		public override XPathNodeIterator SelectChildren (string name, string namespaceURI) {
+			if (current.Object is Literal) throw new InvalidOperationException("The navigator is positioned on a literal element.");
+			return new XPathSemWebNavigator((Entity)current.Object, model, nsmgr, namespaceURI + name).SelectChildren(XPathNodeType.All);
+		}
+		
+		public override XPathNodeIterator Select (XPathExpression expr) {
+			expr.SetContext(nswrap);
+			return base.Select(expr);
+		}
+		
+		public override object Evaluate (XPathExpression expr) {
+			expr.SetContext(nswrap);
+			return base.Evaluate(expr);
+		}
+		
+		public override object Evaluate (XPathExpression expr, XPathNodeIterator context) {
+			expr.SetContext(nswrap);
+			return base.Evaluate(expr, context);
+		}
+	}
+}

Modified: trunk/src/MetadataStore.cs
==============================================================================
--- trunk/src/MetadataStore.cs	(original)
+++ trunk/src/MetadataStore.cs	Fri May 16 18:52:24 2008
@@ -349,5 +349,13 @@
 				return false;
 			}
 		}			
+
+		public void DumpNode (XPathSemWebNavigator navi, int depth)
+		{
+			do { 
+				System.Console.WriteLine ("node [{0}] {1} {2}", depth, navi.Name, navi.Value);
+			} while (navi.MoveToNext ());
+		}
+	       
 	}	       
 }

Modified: trunk/src/f-spot.in
==============================================================================
--- trunk/src/f-spot.in	(original)
+++ trunk/src/f-spot.in	Fri May 16 18:52:24 2008
@@ -48,7 +48,7 @@
 	x--uninstalled)
 	    echo "*** Running uninstalled f-spot ***"
 	    EXE_TO_RUN="./f-spot.exe"
-	    export MONO_PATH= PATH_DBUS@@PATH_SEMWEB@@PATH_GIO   /Tao/Tao.OpenGl:../Tao/Tao.OpenGl.Glu:../Tao/Tao.OpenGl.ExtensionLoader:../google-sharp:../gnome-keyring-sharp:../FlickrNet:../SmugMugNet:../libgphoto2-sharp:../semweb:../glitz-sharp/src/:../mono-addins/Mono.Addins:../mono-addins/Mono.Addins.Setup:../mono-addins/Mono.Addins.Gui:$MONO_PATH
+	    export MONO_PATH= PATH_DBUS@@PATH_GIO   /Tao/Tao.OpenGl:../Tao/Tao.OpenGl.Glu:../Tao/Tao.OpenGl.ExtensionLoader:../google-sharp:../gnome-keyring-sharp:../FlickrNet:../SmugMugNet:../libgphoto2-sharp:../semweb:../glitz-sharp/src/:../mono-addins/Mono.Addins:../mono-addins/Mono.Addins.Setup:../mono-addins/Mono.Addins.Gui:$MONO_PATH
 	    ;;
     esac
 done



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