f-spot r3920 - in trunk: . semweb src
- From: sdelcroix svn gnome org
- To: svn-commits-list gnome org
- Subject: f-spot r3920 - in trunk: . semweb src
- Date: Fri, 16 May 2008 19:52:25 +0100 (BST)
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]