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) { 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 : SelectableSource { SelectableSource source; System.IO.TextWriter output; public DebuggedSource(SelectableSource source, System.IO.TextWriter output) { this.source = source; this.output = output; } public bool Distinct { get { return source.Distinct; } } public void Select(StatementSink sink) { Select(Statement.All, sink); } 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 class CachedSource : SelectableSource { SelectableSource source; 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 void Select(StatementSink sink) { Select(Statement.All, sink); } public bool Contains(Statement template) { if (!containsresults.ContainsKey(template)) containsresults[template] = source.Contains(template); return (bool)containsresults[template]; } public void Select(Statement template, StatementSink sink) { if (!selectresults.ContainsKey(template)) { MemoryStore s = new MemoryStore(); source.Select(template, s); selectresults[template] = s; } ((MemoryStore)selectresults[template]).Select(sink); } public void Select(SelectFilter filter, StatementSink sink) { if (!selfilterresults.ContainsKey(filter)) { MemoryStore s = new MemoryStore(); source.Select(filter, s); selfilterresults[filter] = s; } ((MemoryStore)selfilterresults[filter]).Select(sink); } } }