context: using System;
  using System.Collections;
  using System.Data;
  
  using SemWeb.Util;
  
  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) {
  
gt: 			return new SelectResult.Multi(this, filter);