Dont reindex already indexed files. Yet another bug uncovered by the DateTime fixes.
[beagle.git] / Util / SemWeb / Resource.cs
blobcdc59188789bb9d98db51822fe8b836d6201d62c
1 using System;
2 using System.Collections;
4 namespace SemWeb {
6 public abstract class Resource {
7 internal ArrayList extraKeys;
9 internal class ExtraKey {
10 public object Key;
11 public object Value;
14 public abstract string Uri { get; }
16 internal Resource() {
19 public override string ToString() {
20 if (Uri != null) return Uri;
21 return "_";
24 // These get rid of the warning about overring ==, !=.
25 // Since Entity and Literal override these, we're ok.
26 public override bool Equals(object other) {
27 return base.Equals(other);
29 public override int GetHashCode() {
30 return base.GetHashCode();
33 public static bool operator ==(Resource a, Resource b) {
34 if ((object)a == null && (object)b == null) return true;
35 if ((object)a == null || (object)b == null) return false;
36 return a.Equals(b);
38 public static bool operator !=(Resource a, Resource b) {
39 return !(a == b);
42 internal object GetResourceKey(object key) {
43 if (extraKeys == null) return null;
44 for (int i = 0; i < extraKeys.Count; i++) {
45 Resource.ExtraKey ekey = (Resource.ExtraKey)extraKeys[i];
46 if (ekey.Key == key)
47 return ekey.Value;
49 return null;
51 internal void SetResourceKey(object key, object value) {
52 if (extraKeys == null) extraKeys = new ArrayList();
54 foreach (Resource.ExtraKey ekey in extraKeys)
55 if (ekey.Key == key) { extraKeys.Remove(ekey); break; }
57 Resource.ExtraKey k = new Resource.ExtraKey();
58 k.Key = key;
59 k.Value = value;
61 extraKeys.Add(k);
66 public sealed class Entity : Resource {
67 private string uri;
68 int cachedHashCode = -1;
70 public Entity(string uri) { if (uri != null) this.uri = string.Intern(uri); }
72 public override string Uri {
73 get {
74 return uri;
78 public static implicit operator Entity(string uri) { return new Entity(uri); }
80 public override int GetHashCode() {
81 if (cachedHashCode != -1) return cachedHashCode;
83 if (Uri != null) {
84 cachedHashCode = Uri.GetHashCode();
85 } else if (extraKeys != null && extraKeys.Count == 1) {
86 ExtraKey v = (ExtraKey)extraKeys[0];
87 cachedHashCode = unchecked(v.Key.GetHashCode() + v.Value.GetHashCode());
90 // If there's no Uri or ExtraKeys info, then this
91 // object is only equal to itself. It's then safe
92 // to use object.GetHashCode().
93 if (cachedHashCode == -1) cachedHashCode = base.GetHashCode();
95 return cachedHashCode;
98 public override bool Equals(object other) {
99 if (object.ReferenceEquals(this, other)) return true;
100 if (!(other is Entity)) return false;
102 // If anonymous, then we have to compare extraKeys.
103 if ((Uri == null && ((Resource)other).Uri == null)) {
104 ArrayList otherkeys = ((Resource)other).extraKeys;
105 if (otherkeys != null && extraKeys != null) {
106 for (int vi1 = 0; vi1 < extraKeys.Count; vi1++) {
107 ExtraKey v1 = (ExtraKey)extraKeys[vi1];
108 for (int vi2 = 0; vi2 < otherkeys.Count; vi2++) {
109 ExtraKey v2 = (ExtraKey)otherkeys[vi2];
110 if (v1.Key == v2.Key)
111 return v1.Value.Equals(v2.Value);
116 return false;
119 return ((Resource)other).Uri != null && ((Resource)other).Uri == Uri;
122 // Although these do the same as Resource's operator overloads,
123 // having these plus the implict string conversion allows
124 // these operators to work with entities and strings.
126 public static bool operator ==(Entity a, Entity b) {
127 if ((object)a == null && (object)b == null) return true;
128 if ((object)a == null || (object)b == null) return false;
129 return a.Equals(b);
131 public static bool operator !=(Entity a, Entity b) {
132 return !(a == b);
136 public sealed class Literal : Resource {
137 private string value, lang, type;
139 public Literal(string value) : this(value, null, null) {
142 public Literal(string value, string language, string dataType) {
143 if (value == null)
144 throw new ArgumentNullException("value");
145 this.value = string.Intern(value);
146 this.lang = language;
147 this.type = dataType;
150 public static explicit operator Literal(string value) { return new Literal(value); }
152 public override string Uri { get { return null; } }
154 public string Value { get { return value; } }
155 public string Language { get { return lang; } }
156 public string DataType { get { return type; } }
158 public override bool Equals(object other) {
159 if (other == null) return false;
160 if (!(other is Literal)) return false;
161 Literal literal = (Literal)other;
162 if (Value != literal.Value) return false;
163 if (different(Language, literal.Language)) return false;
164 if (different(DataType, literal.DataType)) return false;
165 return true;
168 private bool different(string a, string b) {
169 if ((object)a == (object)b) return false;
170 if (a == null || b == null) return true;
171 return a != b;
174 public override int GetHashCode() {
175 return Value.GetHashCode();
178 public override string ToString() {
179 System.Text.StringBuilder ret = new System.Text.StringBuilder();
180 ret.Append('"');
181 ret.Append(N3Writer.Escape(Value));
182 ret.Append('"');
184 if (Language != null) {
185 ret.Append('@');
186 ret.Append(N3Writer.Escape(Language));
189 if (DataType != null) {
190 ret.Append("^^<");
191 ret.Append(N3Writer.Escape(DataType));
192 ret.Append(">");
194 return ret.ToString();
197 public static Literal Parse(string literal, NamespaceManager namespaces) {
198 if (literal.Length < 2 || literal[0] != '\"') throw new FormatException("Literal value must start with a quote.");
199 int quote = literal.LastIndexOf('"');
200 if (quote <= 0) throw new FormatException("Literal value must have an end quote (" + literal + ")");
201 string value = literal.Substring(1, quote-1);
202 literal = literal.Substring(quote+1);
204 value = value.Replace("\\\"", "\"");
205 value = value.Replace("\\\\", "\\");
207 string lang = null;
208 string datatype = null;
210 if (literal.Length >= 2 && literal[0] == '@') {
211 int type = literal.IndexOf("^^");
212 if (type == -1) lang = literal.Substring(1);
213 else {
214 lang = literal.Substring(1, type);
215 literal = literal.Substring(type);
219 if (literal.StartsWith("^^")) {
220 if (literal.StartsWith("^^<") && literal.EndsWith(">")) {
221 datatype = literal.Substring(3, literal.Length-4);
222 } else {
223 if (namespaces == null)
224 throw new ArgumentException("No NamespaceManager was given to resolve the QName in the literal string.");
225 datatype = namespaces.Resolve(literal.Substring(2));
229 return new Literal(value, lang, datatype);
234 public abstract class LiteralFilter : Resource {
235 public LiteralFilter() : base(null) { }
237 public override string Uri { get { return null; } }
239 public abstract bool Matches(Literal literal);
242 public interface SQLLiteralFilter {
243 string GetSQLFunction();
246 public class LiteralNumericComparison : LiteralFilter, SQLLiteralFilter {
247 double value;
248 Op comparison;
250 public LiteralNumericComparison(double value, Op comparison) {
251 this.value = value; this.comparison = comparison;
254 public enum Op {
255 Equal,
256 NotEqual,
257 GreaterThan,
258 GreaterThanOrEqual,
259 LessThan,
260 LessThanOrEqual,
263 public override bool Matches(Literal literal) {
264 double v;
265 if (!double.TryParse(literal.Value, System.Globalization.NumberStyles.Any, null, out v)) return false;
267 switch (comparison) {
268 case Op.Equal: return v == value;
269 case Op.NotEqual: return v != value;
270 case Op.GreaterThan: return v > value;
271 case Op.GreaterThanOrEqual: return v >= value;
272 case Op.LessThan: return v < value;
273 case Op.LessThanOrEqual: return v <= value;
274 default: return false;
278 public string GetSQLFunction() {
279 switch (comparison) {
280 case Op.Equal: return "literal = " + value;
281 case Op.NotEqual: return "literal != " + value;
282 case Op.GreaterThan: return "literal > " + value;
283 case Op.GreaterThanOrEqual: return "literal >= " + value;
284 case Op.LessThan: return "literal < " + value;
285 case Op.LessThanOrEqual: return "literal <= " + value;
286 default: return null;
291 public class LiteralStringComparison : LiteralFilter, SQLLiteralFilter {
292 string value;
293 Op comparison;
295 public LiteralStringComparison(string value, Op comparison) {
296 this.value = value; this.comparison = comparison;
299 public enum Op {
300 Equal,
301 NotEqual,
302 GreaterThan,
303 GreaterThanOrEqual,
304 LessThan,
305 LessThanOrEqual,
308 public override bool Matches(Literal literal) {
309 string v = literal.Value;
311 switch (comparison) {
312 case Op.Equal: return v == value;
313 case Op.NotEqual: return v != value;
314 case Op.GreaterThan: return v.CompareTo(value) > 0;
315 case Op.GreaterThanOrEqual: return v.CompareTo(value) >= 0;
316 case Op.LessThan: return v.CompareTo(value) < 0;
317 case Op.LessThanOrEqual: return v.CompareTo(value) <= 0;
318 default: return false;
322 public string GetSQLFunction() {
323 switch (comparison) {
324 case Op.Equal: return "literal = " + value;
325 case Op.NotEqual: return "literal != " + value;
326 case Op.GreaterThan: return "literal > " + value;
327 case Op.GreaterThanOrEqual: return "literal >= " + value;
328 case Op.LessThan: return "literal < " + value;
329 case Op.LessThanOrEqual: return "literal <= " + value;
330 default: return null;
337 namespace SemWeb.Bind {
338 public class Any {
339 Entity ent;
340 Store model;
342 public Any(Entity entity, Store model) {
343 this.ent = entity;
344 this.model = model;
347 public Entity Entity { get { return ent; } }
348 public Store Model { get { return model; } }
350 public string Uri { get { return ent.Uri; } }
352 private Resource toRes(object value) {
353 if (value == null) return null;
354 if (value is Resource) return (Resource)value; // shouldn't happen
355 if (value is string) return new Literal((string)value);
356 if (value is Any) return ((Any)value).ent;
357 throw new ArgumentException("value is not of a recognized type");
360 protected void AddValue(Entity predicate, object value, bool forward) {
361 if (value == null) throw new ArgumentNullException("value");
362 Resource v = toRes(value);
363 if (!forward && !(v is Entity)) throw new ArgumentException("Cannot set this property to a literal value.");
364 Statement add = new Statement(ent, predicate, v);
365 if (!forward) add = add.Invert();
366 model.Add(add);
368 protected void RemoveValue(Entity predicate, object value, bool forward) {
369 if (value == null) throw new ArgumentNullException("value");
370 Resource v = toRes(value);
371 if (!forward && !(v is Entity)) throw new ArgumentException("Cannot set this property to a literal value.");
372 Statement rem = new Statement(ent, predicate, v);
373 if (!forward) rem = rem.Invert();
374 model.Remove(rem);
377 protected void SetFuncProperty(Entity predicate, object value, bool forward) {
378 Resource v = toRes(value);
379 Statement search = new Statement(ent, predicate, null);
380 Statement replace = new Statement(ent, predicate, v);
381 if (!forward) {
382 if (v != null && !(v is Entity)) throw new ArgumentException("Cannot set this property to a literal value.");
383 search = search.Invert();
384 replace = replace.Invert();
387 if (v != null) {
388 foreach (Statement s in model.Select(search)) {
389 model.Replace(s, replace);
390 return;
392 model.Add(replace);
393 } else {
394 model.Remove(search);
398 protected void SetNonFuncProperty(Entity predicate, object[] values, bool forward) {
399 Statement search = new Statement(ent, predicate, null);
400 if (!forward)
401 search = search.Invert();
403 model.Remove(search);
404 if (values != null) {
405 foreach (object value in values) {
406 Resource v = toRes(value);
407 if (v == null) throw new ArgumentNullException("element of values array");
408 if (!forward && !(v is Entity)) throw new ArgumentException("Cannot set this property to a literal value.");
409 Statement add = new Statement(ent, predicate, v);
410 if (!forward) add = add.Invert();
411 model.Add(add);