2 using System
.Collections
;
9 namespace SemWeb
.Query
{
10 public class RSquary
: QueryEngine
{
12 // TODO: Optional statements
13 // TODO: Grouping and disjunctions
15 public static Entity qSelect
= "http://purl.oclc.org/NET/rsquary/select";
16 public static Entity qLimit
= "http://purl.oclc.org/NET/rsquary/returnLimit";
17 public static Entity qStart
= "http://purl.oclc.org/NET/rsquary/returnStart";
18 public static Entity qDistinctFrom
= "http://purl.oclc.org/NET/rsquary/distinctFrom";
19 public static Entity qOptional
= "http://purl.oclc.org/NET/rsquary/optional";
21 public RSquary(Store queryModel
, string queryUri
) : this(queryModel
, queryUri
, null) {
24 public RSquary(Store queryModel
, string queryUri
, Hashtable extraValueFilters
) {
25 Entity query
= new Entity(queryUri
);
27 // Find the query options
28 ReturnStart
= GetIntOption(queryModel
, query
, qStart
);
29 ReturnLimit
= GetIntOption(queryModel
, query
, qLimit
);
31 // Search the query for 'select'
32 foreach (Resource r
in queryModel
.SelectObjects(query
, qSelect
)) {
33 if (!(r
is Entity
)) throw new QueryException("Query variables cannot be literals.");
37 // Search the query for 'distinct' predicates between variables.
38 foreach (Statement s
in queryModel
.Select(new Statement(null, qDistinctFrom
, null))) {
39 if (!(s
.Object
is Entity
)) throw new QueryException("The distinctFrom predicate cannot have a literal as its object.");
40 MakeDistinct(s
.Subject
, (Entity
)s
.Object
);
43 // Add all statements except the query predicates and value filters into a
44 // new store with just the statements relevant to the search.
45 foreach (Statement s
in queryModel
.Select(new Statement(null,null,null))) {
46 if (IsQueryPredicate(s
.Predicate
)) continue;
48 if (s
.Predicate
.Uri
!= null && extraValueFilters
!= null && extraValueFilters
.ContainsKey(s
.Predicate
.Uri
)) {
49 ValueFilterFactory f
= (ValueFilterFactory
)extraValueFilters
[s
.Predicate
.Uri
];
50 AddValueFilter(s
.Subject
, f
.GetValueFilter(s
.Predicate
.Uri
, s
.Object
));
53 ValueFilter f
= ValueFilter
.GetValueFilter(s
.Predicate
, s
.Object
);
55 AddValueFilter(s
.Subject
, f
);
60 if (s
.Meta
== Statement
.DefaultMeta
)
62 else if (queryModel
.Contains(new Statement(query
, qOptional
, s
.Meta
)))
67 private int GetIntOption(Store queryModel
, Entity query
, Entity predicate
) {
68 Resource
[] rr
= queryModel
.SelectObjects(query
, predicate
);
69 if (rr
.Length
== 0) return -1;
71 if (r
== null || !(r
is Literal
)) return -1;
73 return int.Parse(((Literal
)r
).Value
);
74 } catch (Exception e
) {
75 throw new QueryException("Invalid integer value for <" + predicate
+ ">, '" + ((Literal
)r
).Value
+ "'.", e
);
79 private bool IsQueryPredicate(Entity e
) {
80 if (e
== qSelect
) return true;
81 if (e
== qDistinctFrom
) return true;
82 if (e
== qLimit
) return true;
83 if (e
== qStart
) return true;
84 if (e
== qOptional
) return true;
89 public class PrintQuerySink
: QueryResultSink
{
90 public override void Init(Entity
[] variables
) { }
91 public override void Finished() { }
92 public override bool Add(VariableBinding
[] result
) {
93 foreach (VariableBinding
var in result
)
94 if (var.Variable
.Uri
!= null && var.Target
!= null)
95 Console
.WriteLine(var.Variable
+ " ==> " + var.Target
.ToString());
101 public class HTMLQuerySink
: QueryResultSink
{
104 public HTMLQuerySink(TextWriter output
) { this.output = output; }
106 public override void Init(Entity
[] variables
) {
107 output
.WriteLine("<tr>");
108 foreach (Entity
var in variables
)
110 output
.WriteLine("<th>" + var + "</th>");
111 output
.WriteLine("</tr>");
114 public override void Finished() { }
116 public override bool Add(VariableBinding
[] result
) {
117 output
.WriteLine("<tr>");
118 foreach (VariableBinding
var in result
) {
119 if (var.Variable
.Uri
== null) continue;
120 string t
= var.Target
.ToString();
121 if (var.Target
is Literal
) t
= ((Literal
)var.Target
).Value
;
122 output
.WriteLine("<td>" + t
+ "</td>");
124 output
.WriteLine("</tr>");
129 public class SQLQuerySink
: QueryResultSink
{
133 public SQLQuerySink(TextWriter output
, string table
) { this.output = output; this.table = table; }
135 public override void Finished() { }
137 private string GetFieldType(string datatype
) {
139 case "http://www.w3.org/2001/XMLSchema#string":
140 case "http://www.w3.org/2001/XMLSchema#normalizedString":
143 case "http://www.w3.org/2001/XMLSchema#float":
146 case "http://www.w3.org/2001/XMLSchema#double":
147 return "DOUBLE PRECISION";
149 case "http://www.w3.org/2001/XMLSchema#decimal":
152 case "http://www.w3.org/2001/XMLSchema#integer":
153 case "http://www.w3.org/2001/XMLSchema#nonPositiveInteger":
154 case "http://www.w3.org/2001/XMLSchema#negativeInteger":
155 case "http://www.w3.org/2001/XMLSchema#int":
156 case "http://www.w3.org/2001/XMLSchema#short":
159 case "http://www.w3.org/2001/XMLSchema#long":
163 case "http://www.w3.org/2001/XMLSchema#boolean":
164 case "http://www.w3.org/2001/XMLSchema#byte":
165 case "http://www.w3.org/2001/XMLSchema#unsignedByte":
168 case "http://www.w3.org/2001/XMLSchema#nonNegativeInteger":
169 case "http://www.w3.org/2001/XMLSchema#unsignedInt":
170 case "http://www.w3.org/2001/XMLSchema#unsignedShort":
171 case "http://www.w3.org/2001/XMLSchema#positiveInteger":
172 return "UNSIGNED INT";
174 case "http://www.w3.org/2001/XMLSchema#unsignedLong":
175 return "UNSIGNED BIGINT";
177 case "http://www.w3.org/2001/XMLSchema#dateTime":
180 case "http://www.w3.org/2001/XMLSchema#date":
183 case "http://www.w3.org/2001/XMLSchema#time":
184 case "http://www.w3.org/2001/XMLSchema#duration":
187 case "http://www.w3.org/2001/XMLSchema#base64Binary":
190 case "http://www.w3.org/2001/XMLSchema#anyURI":
191 // shouldn't be case-insensitive, but using BLOB
192 // instead seems to make things too complex.
199 public override void Init(Entity
[] variables
) {
200 output
.Write("CREATE TABLE " + table
+ " (");
203 foreach (Entity
var in variables
) {
204 if (var.Uri
== null) continue;
206 int hash
= var.Uri
.LastIndexOf("#");
207 if (hash
== -1) name
= "`" + var.Uri
+ "`";
208 else name
= var.Uri
.Substring(hash
+1);
210 string type
= "BLOB";
211 //if (var.Target is Literal && ((Literal)var.Target).DataType != null)
212 // type = GetFieldType(((Literal)var.Target).DataType);
214 if (!f
) { output.Write(", "); } f
= false;
215 output
.Write(name
+ " " + type
);
218 output
.WriteLine(");");
221 public override bool Add(VariableBinding
[] result
) {
222 output
.Write("INSERT INTO " + table
+ " VALUES (");
224 foreach (VariableBinding
var in result
) {
225 if (var.Variable
.Uri
== null) continue;
227 if (!firstx
) { output.Write(", "); } firstx
= false;
228 if (var.Target
== null)
229 output
.Write("NULL");
230 else if (var.Target
is Literal
)
231 output
.Write(Escape(((Literal
)var.Target
).Value
));
232 else if (var.Target
.Uri
!= null)
233 output
.Write("\"" + var.Target
.Uri
+ "\"");
235 output
.Write("\"\"");
237 output
.WriteLine(");");
242 private string Escape(string str
) {
243 if (str
== null) return "NULL";
244 return "\"" + EscapeUnquoted(str
) + "\"";
247 StringBuilder EscapeUnquotedBuffer
= new StringBuilder();
248 private string EscapeUnquoted(string str
) {
249 StringBuilder b
= EscapeUnquotedBuffer
;
258 public class SparqlXmlQuerySink
: QueryResultSink
{
259 System
.Xml
.XmlWriter output
;
260 string variableNamespace
;
262 int blankNodeCounter
= 0;
263 Hashtable blankNodes
= new Hashtable();
265 private static System
.Xml
.XmlWriter
GetWriter(System
.IO
.TextWriter writer
) {
266 System
.Xml
.XmlTextWriter w
= new System
.Xml
.XmlTextWriter(writer
);
267 w
.Formatting
= System
.Xml
.Formatting
.Indented
;
271 public SparqlXmlQuerySink(TextWriter output
, string variableNamespace
)
272 : this(GetWriter(output
), variableNamespace
) {
275 public SparqlXmlQuerySink(System
.Xml
.XmlWriter output
, string variableNamespace
) {
276 this.output
= output
;
277 this.variableNamespace
= variableNamespace
;
278 output
.WriteStartElement("sparql");
279 output
.WriteAttributeString("xmlns", "http://www.w3.org/2001/sw/DataAccess/rf1/result");
280 output
.WriteStartElement("head");
283 string GetName(string uri
) {
284 if (uri
.StartsWith(variableNamespace
))
285 uri
= uri
.Substring(variableNamespace
.Length
);
286 if (uri
.StartsWith("?") || uri
.StartsWith("$"))
287 uri
= uri
.Substring(1);
291 public override void Init(Entity
[] variables
) {
292 foreach (Entity
var in variables
) {
293 if (var.Uri
== null) continue;
294 output
.WriteStartElement("variable");
295 output
.WriteAttributeString("name", GetName(var.Uri
));
296 output
.WriteEndElement();
298 output
.WriteEndElement(); // head
299 output
.WriteStartElement("results");
302 public override bool Add(VariableBinding
[] result
) {
303 output
.WriteStartElement("result");
304 foreach (VariableBinding
var in result
) {
305 if (var.Variable
.Uri
== null) continue;
307 output
.WriteStartElement(GetName(var.Variable
.Uri
));
308 if (var.Target
== null) {
309 output
.WriteAttributeString("bound", "false");
310 } else if (var.Target
.Uri
!= null) {
311 output
.WriteAttributeString("uri", var.Target
.Uri
);
312 } else if (var.Target
is Literal
) {
313 Literal literal
= (Literal
)var.Target
;
314 if (literal
.DataType
!= null)
315 output
.WriteAttributeString("datatype", literal
.DataType
);
316 if (literal
.Language
!= null)
317 output
.WriteAttributeString("language", literal
.Language
);
318 output
.WriteString(literal
.Value
);
321 if (blankNodes
.ContainsKey(var.Target
))
322 id
= (string)blankNodes
[var.Target
];
324 id
= "r" + (++blankNodeCounter
);
325 blankNodes
[var.Target
] = id
;
327 output
.WriteAttributeString("bnodeid", id
);
330 output
.WriteEndElement();
332 output
.WriteEndElement();
337 public override void Finished() {
338 output
.WriteEndElement(); // results
339 output
.WriteEndElement(); // sparql