2 using System
.Collections
;
9 namespace SemWeb
.Query
{
10 public class QueryException
: ApplicationException
{
11 public QueryException(string message
) : base(message
) {
14 public QueryException(string message
, Exception cause
) : base(message
, cause
) {
18 public class QueryEngine
{
21 ArrayList setupVariables
= new ArrayList();
22 ArrayList setupVariablesDistinct
= new ArrayList();
23 ArrayList setupValueFilters
= new ArrayList();
24 ArrayList setupStatements
= new ArrayList();
25 ArrayList setupOptionalStatements
= new ArrayList();
30 // Query model information
33 object sync
= new object();
35 Entity
[] variableEntities
;
36 QueryStatement
[][] statements
;
38 // contains functional and inverse functional properties
39 ResSet fps
= new ResSet(),
42 private struct Variable
{
44 public ValueFilter
[] Filters
;
47 private struct VarOrAnchor
{
48 public bool IsVariable
;
50 public Resource Anchor
;
51 public Resource
[] ArrayOfAnchor
;
53 public override string ToString() {
55 return "?" + VarIndex
;
57 return Anchor
.ToString();
61 private class QueryStatement
{ // class because of use with IComparer
68 public int NumVars() {
69 return (Subject
.IsVariable
? 1 : 0)
70 + (Predicate
.IsVariable
? 1 : 0)
71 + (Object
.IsVariable
? 1 : 0);
74 public override string ToString() {
75 return Subject
+ " " + Predicate
+ " " + Object
;
80 public ResSet
[] Bindings
;
81 public QueryResult(QueryEngine q
) {
82 Bindings
= new ResSet
[q
.variables
.Length
];
84 private QueryResult(int x
) {
85 Bindings
= new ResSet
[x
];
87 public void Add(QueryStatement qs
, Statement bs
) {
88 if (qs
.Subject
.IsVariable
) Add(qs
.Subject
.VarIndex
, bs
.Subject
);
89 if (qs
.Predicate
.IsVariable
) Add(qs
.Predicate
.VarIndex
, bs
.Predicate
);
90 if (qs
.Object
.IsVariable
) Add(qs
.Object
.VarIndex
, bs
.Object
);
92 void Add(int varIndex
, Resource binding
) {
93 if (Bindings
[varIndex
] == null) Bindings
[varIndex
] = new ResSet();
94 Bindings
[varIndex
].Add(binding
);
96 public void Clear(QueryStatement qs
) {
97 if (qs
.Subject
.IsVariable
&& Bindings
[qs
.Subject
.VarIndex
] != null) Bindings
[qs
.Subject
.VarIndex
].Clear();
98 if (qs
.Predicate
.IsVariable
&& Bindings
[qs
.Predicate
.VarIndex
] != null) Bindings
[qs
.Predicate
.VarIndex
].Clear();
99 if (qs
.Object
.IsVariable
&& Bindings
[qs
.Object
.VarIndex
] != null) Bindings
[qs
.Object
.VarIndex
].Clear();
101 public void Set(QueryStatement qs
, Statement bs
) {
102 if (qs
.Subject
.IsVariable
) Set(qs
.Subject
.VarIndex
, bs
.Subject
);
103 if (qs
.Predicate
.IsVariable
) Set(qs
.Predicate
.VarIndex
, bs
.Predicate
);
104 if (qs
.Object
.IsVariable
) Set(qs
.Object
.VarIndex
, bs
.Object
);
106 void Set(int varIndex
, Resource binding
) {
107 if (Bindings
[varIndex
] == null) Bindings
[varIndex
] = new ResSet();
108 else Bindings
[varIndex
].Clear();
109 Bindings
[varIndex
].Add(binding
);
111 public QueryResult
Clone() {
112 QueryResult r
= new QueryResult(Bindings
.Length
);
113 for (int i
= 0; i
< Bindings
.Length
; i
++)
114 if (Bindings
[i
] != null)
115 r
.Bindings
[i
] = Bindings
[i
].Clone();
121 public ArrayList Results
= new ArrayList();
122 public QueryResult Union
;
124 public BindingSet(QueryEngine q
) {
125 Union
= new QueryResult(q
);
129 public void Select(Entity entity
) {
130 if (entity
.Uri
== null) throw new QueryException("Anonymous nodes are automatically considered variables.");
131 if (setupVariables
.Contains(entity
)) return;
132 setupVariables
.Add(entity
);
135 public void MakeDistinct(Entity a
, Entity b
) {
136 SetupVariablesDistinct d
= new SetupVariablesDistinct();
139 setupVariablesDistinct
.Add(d
);
142 public void AddValueFilter(Entity entity
, ValueFilter filter
) {
143 SetupValueFilter d
= new SetupValueFilter();
146 setupValueFilters
.Add(d
);
149 public void AddFilter(Statement filter
) {
150 setupStatements
.Add(filter
);
153 public void AddOptionalFilter(Statement filter
) {
154 setupOptionalStatements
.Add(filter
);
157 public int ReturnStart { get { return start; }
set { start = value; }
}
159 public int ReturnLimit { get { return limit; }
set { limit = value; }
}
161 private class SetupVariablesDistinct
{
164 private class SetupValueFilter
{
166 public ValueFilter b
;
169 public void Query(Store targetModel
, QueryResultSink result
) {
177 result
.Init(variableEntities
);
179 BindingSet bindings
= new BindingSet(this);
180 foreach (QueryStatement
[] group in statements
) {
181 bool ret
= Query(group, bindings
, targetModel
);
183 // A false return value indicates the query
190 VariableBinding
[] finalbindings
= new VariableBinding
[variables
.Length
];
191 for (int i
= 0; i
< variables
.Length
; i
++)
192 finalbindings
[i
].Variable
= variableEntities
[i
];
195 foreach (QueryResult r
in bindings
.Results
) {
196 Permutation permutation
= new Permutation(r
.Bindings
);
199 if (ctr
< start
) continue;
200 for (int i
= 0; i
< variables
.Length
; i
++)
201 finalbindings
[i
].Target
= permutation
[i
];
202 result
.Add(finalbindings
);
203 if (ctr
== start
+limit
) break;
204 } while (permutation
.Next());
205 if (ctr
== start
+limit
) break;
213 public Resource
[][] values
;
215 public Resource
this[int i
] {
217 return values
[i
][index
[i
]];
221 public Permutation(ResSet
[] bindings
) {
222 index
= new int[bindings
.Length
];
223 values
= new Resource
[bindings
.Length
][];
224 for (int i
= 0; i
< bindings
.Length
; i
++) {
225 values
[i
] = new Resource
[bindings
[i
] == null ? 1 : bindings
[i
].Count
];
226 if (bindings
[i
] != null) {
228 foreach (Resource r
in bindings
[i
])
229 values
[i
][ctr
++] = r
;
234 for (int i
= 0; i
< index
.Length
; i
++) {
236 if (index
[i
] < values
[i
].Length
) break;
239 if (i
== index
.Length
-1) return false;
245 private void Debug(string message
) {
246 //Console.Error.WriteLine(message);
249 class BindingEnumerator
{
250 IEnumerator
[] loops
= new IEnumerator
[3];
253 public BindingEnumerator(QueryStatement qs
, QueryResult bindings
) {
254 loops
[0] = GetBindings(qs
.Subject
, bindings
);
255 loops
[1] = GetBindings(qs
.Predicate
, bindings
);
256 loops
[2] = GetBindings(qs
.Object
, bindings
);
259 public bool MoveNext(out Entity s
, out Entity p
, out Resource o
) {
261 bool b
= loops
[loop
].MoveNext();
263 if (loop
== 0) { s = null; p = null; o = null; return false; }
270 object obj
= loops
[loop
].Current
;
271 if (obj
!= null && !(obj
is Entity
)) continue;
274 if (loop
< 2) { loop++; continue; }
276 s
= loops
[0].Current
as Entity
;
277 p
= loops
[1].Current
as Entity
;
278 o
= loops
[2].Current
as Resource
;
284 private bool Query(QueryStatement
[] group, BindingSet bindings
, Store targetModel
) {
285 if (group.Length
== 1) {
286 QueryStatement qs
= group[0];
288 int numMultiplyBound
= IsMultiplyBound(qs
.Subject
, bindings
)
289 + IsMultiplyBound(qs
.Predicate
, bindings
)
290 + IsMultiplyBound(qs
.Object
, bindings
);
292 if (numMultiplyBound
>= 1) {
293 // If there is one or more multiply-bound variable,
294 // then we need to iterate through the permutations
295 // of the variables in the statement.
297 Debug(qs
.ToString() + " Something Multiply Bound");
302 ArrayList templates
= new ArrayList();
303 BindingEnumerator enumer
= new BindingEnumerator(qs
, bindings
.Union
);
304 while (enumer
.MoveNext(out s
, out p
, out o
))
305 templates
.Add(new Statement(s
, p
, o
));
307 Debug("\t" + templates
.Count
+ " Templates");
309 // But we still need to preserve the pairings of
310 // the multiply bound variable with the matching
313 MemoryStore matches1
= targetModel
.Select((Statement
[])templates
.ToArray(typeof(Statement
)));
315 Debug("\t" + matches1
.StatementCount
+ " Matches");
317 // The memory store that we get back from a select
318 // won't do indexing, but indexing will help.
319 MemoryStore matches
= new MemoryStore();
320 matches
.Import(matches1
);
322 ArrayList newbindings
= new ArrayList();
324 if (!qs
.Optional
) bindings
.Union
.Clear(qs
);
326 foreach (QueryResult binding
in bindings
.Results
) {
327 // Break apart the permutations in this binding.
328 BindingEnumerator enumer2
= new BindingEnumerator(qs
, binding
);
329 while (enumer2
.MoveNext(out s
, out p
, out o
)) {
330 // Get the matching statements from the union query
331 Statement bs
= new Statement(s
, p
, o
);
332 MemoryStore innermatches
= matches
.Select(bs
);
334 // If no matches, the binding didn't match the filter.
335 if (innermatches
.StatementCount
== 0) {
337 // Preserve the binding.
338 newbindings
.Add(binding
);
340 // Toss out the binding.
345 foreach (Statement m
in innermatches
) {
346 if (!MatchesFilters(m
, qs
, targetModel
)) {
347 if (qs
.Optional
) newbindings
.Add(binding
);
350 bindings
.Union
.Add(qs
, m
);
352 QueryResult r
= binding
.Clone();
359 bindings
.Results
= newbindings
;
362 // There are no multiply bound variables, but if
363 // there are more than two unbound variables,
364 // we need to be sure to preserve the pairings
365 // of the matching values.
367 int numUnbound
= IsUnbound(qs
.Subject
, bindings
)
368 + IsUnbound(qs
.Predicate
, bindings
)
369 + IsUnbound(qs
.Object
, bindings
);
371 bool sunbound
= IsUnbound(qs
.Subject
, bindings
) == 1;
372 bool punbound
= IsUnbound(qs
.Predicate
, bindings
) == 1;
373 bool ounbound
= IsUnbound(qs
.Object
, bindings
) == 1;
375 Statement s
= GetStatement(qs
, bindings
);
377 // If we couldn't get a statement out of this,
378 // then if this was not an optional filter,
379 // fail. If this was optional, don't change
381 if (s
== StatementFailed
) return qs
.Optional
;
383 if (numUnbound
== 0) {
384 Debug(qs
.ToString() + " All bound");
386 // All variables are singly bound already.
387 // We can just test if the statement exists.
388 if (!targetModel
.Contains(s
)
392 } else if (numUnbound
== 1) {
393 Debug(qs
.ToString() + " 1 Unbound");
395 // There is just one unbound variable. The others
396 // are not multiply bound, so they must be uniquely
397 // bound (but they may not be bound in all results).
398 // Run a combined select to find all possible values
399 // of the unbound variable at once, and set these to
400 // be the values of the variable for matching results.
402 ResSet values
= new ResSet();
403 foreach (Statement match
in targetModel
.Select(s
)) {
404 if (!MatchesFilters(match
, qs
, targetModel
)) continue;
405 if (sunbound
) values
.Add(match
.Subject
);
406 if (punbound
) values
.Add(match
.Predicate
);
407 if (ounbound
) values
.Add(match
.Object
);
410 Debug("\t" + values
.Count
+ " matches");
412 if (values
.Count
== 0)
416 if (sunbound
) varIndex
= qs
.Subject
.VarIndex
;
417 if (punbound
) varIndex
= qs
.Predicate
.VarIndex
;
418 if (ounbound
) varIndex
= qs
.Object
.VarIndex
;
420 if (bindings
.Results
.Count
== 0)
421 bindings
.Results
.Add(new QueryResult(this));
423 bindings
.Union
.Bindings
[varIndex
] = new ResSet();
424 foreach (Resource r
in values
)
425 bindings
.Union
.Bindings
[varIndex
].Add(r
);
427 foreach (QueryResult r
in bindings
.Results
) {
428 // Check that the bound variables are bound in this result.
429 // If it is bound, it will be bound to the correct resource,
430 // but it might not be bound at all if an optional statement
431 // failed to match -- in which case, don't modify the binding.
432 if (qs
.Subject
.IsVariable
&& !sunbound
&& r
.Bindings
[qs
.Subject
.VarIndex
] == null) continue;
433 if (qs
.Predicate
.IsVariable
&& !punbound
&& r
.Bindings
[qs
.Predicate
.VarIndex
] == null) continue;
434 if (qs
.Object
.IsVariable
&& !ounbound
&& r
.Bindings
[qs
.Object
.VarIndex
] == null) continue;
436 r
.Bindings
[varIndex
] = values
;
440 // There are two or more unbound variables, the
441 // third variable being uniquely bound, if bound.
442 // Keep track of the pairing of unbound variables.
444 Debug(qs
.ToString() + " 2 or 3 Unbound");
446 if (bindings
.Results
.Count
== 0)
447 bindings
.Results
.Add(new QueryResult(this));
449 ArrayList newbindings
= new ArrayList();
450 foreach (Statement match
in targetModel
.Select(s
)) {
451 if (!MatchesFilters(match
, qs
, targetModel
)) continue;
452 bindings
.Union
.Add(qs
, match
);
453 foreach (QueryResult r
in bindings
.Results
) {
454 if (numUnbound
== 2) {
455 // Check that the bound variable is bound in this result.
456 // If it is bound, it will be bound to the correct resource,
457 // but it might not be bound at all if an optional statement
458 // failed to match -- in which case, preserve the binding if
459 // this was an optional statement.
461 if (qs
.Subject
.IsVariable
&& !sunbound
&& r
.Bindings
[qs
.Subject
.VarIndex
] == null) matches
= false;
462 if (qs
.Predicate
.IsVariable
&& !punbound
&& r
.Bindings
[qs
.Predicate
.VarIndex
] == null) matches
= false;
463 if (qs
.Object
.IsVariable
&& !ounbound
&& r
.Bindings
[qs
.Object
.VarIndex
] == null) matches
= false;
471 QueryResult r2
= r
.Clone();
476 if (qs
.Optional
&& newbindings
.Count
== 0)
477 return true; // don't clear out bindings
478 bindings
.Results
= newbindings
;
483 // There is more than one statement in the group.
484 // These are never optional.
486 Debug("Statement group.");
488 VarOrAnchor
var = new VarOrAnchor();
489 foreach (QueryStatement qs
in group) {
490 if (qs
.Subject
.IsVariable
&& bindings
.Union
.Bindings
[qs
.Subject
.VarIndex
] == null) var = qs
.Subject
;
491 if (qs
.Predicate
.IsVariable
&& bindings
.Union
.Bindings
[qs
.Predicate
.VarIndex
] == null) var = qs
.Predicate
;
492 if (qs
.Object
.IsVariable
&& bindings
.Union
.Bindings
[qs
.Object
.VarIndex
] == null) var = qs
.Object
;
495 // The variable should be unbound so far.
496 if (bindings
.Union
.Bindings
[var.VarIndex
] != null)
497 throw new Exception();
499 ArrayList findstatements
= new ArrayList();
500 foreach (QueryStatement qs
in group) {
501 Statement s
= GetStatement(qs
, bindings
);
502 if (s
== StatementFailed
) return false;
503 findstatements
.Add(s
);
506 ResSet values
= new ResSet();
507 foreach (Entity r
in targetModel
.FindEntities((Statement
[])findstatements
.ToArray(typeof(Statement
)))) {
508 if (!MatchesFilters(r
, var, targetModel
)) continue;
511 if (values
.Count
== 0) return false;
513 bindings
.Union
.Bindings
[var.VarIndex
] = values
;
515 if (bindings
.Results
.Count
== 0)
516 bindings
.Results
.Add(new QueryResult(this));
518 foreach (QueryResult result
in bindings
.Results
)
519 result
.Bindings
[var.VarIndex
] = values
;
525 static Resource
[] ResourceArrayNull
= new Resource
[] { null }
;
527 private static IEnumerator
GetBindings(VarOrAnchor e
, QueryResult bindings
) {
529 if (e
.ArrayOfAnchor
== null)
530 e
.ArrayOfAnchor
= new Resource
[] { e.Anchor }
;
531 return e
.ArrayOfAnchor
.GetEnumerator();
533 if (bindings
.Bindings
[e
.VarIndex
] == null) return ResourceArrayNull
.GetEnumerator();
534 return bindings
.Bindings
[e
.VarIndex
].Items
.GetEnumerator();
536 private int IsMultiplyBound(VarOrAnchor e
, BindingSet bindings
) {
537 if (!e
.IsVariable
) return 0;
538 if (bindings
.Union
.Bindings
[e
.VarIndex
] == null) return 0;
539 if (bindings
.Union
.Bindings
[e
.VarIndex
].Items
.Count
== 1) return 0;
542 private int IsUnbound(VarOrAnchor e
, BindingSet bindings
) {
543 if (!e
.IsVariable
) return 0;
544 if (bindings
.Union
.Bindings
[e
.VarIndex
] == null) return 1;
548 private Resource
GetUniqueBinding(VarOrAnchor e
, BindingSet bindings
) {
549 if (!e
.IsVariable
) return e
.Anchor
;
550 if (bindings
.Union
.Bindings
[e
.VarIndex
] == null || bindings
.Union
.Bindings
[e
.VarIndex
].Count
== 0) return null;
551 if (bindings
.Union
.Bindings
[e
.VarIndex
].Count
> 1) throw new Exception();
552 foreach (Resource r
in bindings
.Union
.Bindings
[e
.VarIndex
].Items
)
554 throw new Exception();
557 Statement StatementFailed
= new Statement(null, null, null);
559 private Statement
GetStatement(QueryStatement sq
, BindingSet bindings
) {
560 Resource s
= GetUniqueBinding(sq
.Subject
, bindings
);
561 Resource p
= GetUniqueBinding(sq
.Predicate
, bindings
);
562 Resource o
= GetUniqueBinding(sq
.Object
, bindings
);
563 if (s
is Literal
|| p
is Literal
) return StatementFailed
;
564 return new Statement((Entity
)s
, (Entity
)p
, o
);
567 bool MatchesFilters(Statement s
, QueryStatement q
, Store targetModel
) {
568 return MatchesFilters(s
.Subject
, q
.Subject
, targetModel
)
569 && MatchesFilters(s
.Predicate
, q
.Predicate
, targetModel
)
570 && MatchesFilters(s
.Object
, q
.Object
, targetModel
);
573 bool MatchesFilters(Resource e
, VarOrAnchor
var, Store targetModel
) {
574 if (!var.IsVariable
) return true;
575 foreach (ValueFilter f
in variables
[var.VarIndex
].Filters
) {
576 if (!f
.Filter(e
, targetModel
)) return false;
581 private void Init() {
582 // Any anonymous nodes in the graph are SelectFirst nodes
583 foreach (Statement s
in setupStatements
) {
584 InitAnonVariable(s
.Subject
);
585 InitAnonVariable(s
.Predicate
);
586 InitAnonVariable(s
.Object
);
588 foreach (Statement s
in setupOptionalStatements
) {
589 InitAnonVariable(s
.Subject
);
590 InitAnonVariable(s
.Predicate
);
591 InitAnonVariable(s
.Object
);
594 // Set up the variables array.
595 variables
= new Variable
[setupVariables
.Count
];
596 variableEntities
= new Entity
[variables
.Length
];
597 Hashtable varIndex
= new Hashtable();
598 for (int i
= 0; i
< variables
.Length
; i
++) {
599 variables
[i
].Entity
= (Entity
)setupVariables
[i
];
600 variableEntities
[i
] = variables
[i
].Entity
;
601 varIndex
[variables
[i
].Entity
] = i
;
603 ArrayList filters
= new ArrayList();
604 foreach (SetupValueFilter filter
in setupValueFilters
) {
605 if (filter
.a
== variables
[i
].Entity
)
606 filters
.Add(filter
.b
);
609 variables
[i
].Filters
= (ValueFilter
[])filters
.ToArray(typeof(ValueFilter
));
612 // Set up the statements
613 ArrayList statements
= new ArrayList();
614 foreach (Statement st
in setupStatements
)
615 InitSetStatement(st
, statements
, varIndex
, false);
616 foreach (Statement st
in setupOptionalStatements
)
617 InitSetStatement(st
, statements
, varIndex
, true);
619 // Order the statements in the most efficient order
620 // for the recursive query.
621 Hashtable setVars
= new Hashtable();
622 ArrayList sgroups
= new ArrayList();
623 while (statements
.Count
> 0) {
624 QueryStatement
[] group = InitBuildNode(statements
, setVars
);
626 foreach (QueryStatement qs
in group) {
627 if (qs
.Subject
.IsVariable
) setVars
[qs
.Subject
.VarIndex
] = setVars
;
628 if (qs
.Predicate
.IsVariable
) setVars
[qs
.Predicate
.VarIndex
] = setVars
;
629 if (qs
.Object
.IsVariable
) setVars
[qs
.Object
.VarIndex
] = setVars
;
633 this.statements
= (QueryStatement
[][])sgroups
.ToArray(typeof(QueryStatement
[]));
636 private void InitAnonVariable(Resource r
) {
637 if (r
is Entity
&& r
.Uri
== null)
638 setupVariables
.Add(r
);
641 private void InitSetStatement(Statement st
, ArrayList statements
, Hashtable varIndex
, bool optional
) {
642 QueryStatement qs
= new QueryStatement();
644 InitSetStatement(st
.Subject
, ref qs
.Subject
, varIndex
);
645 InitSetStatement(st
.Predicate
, ref qs
.Predicate
, varIndex
);
646 InitSetStatement(st
.Object
, ref qs
.Object
, varIndex
);
648 qs
.Optional
= optional
;
650 // If this statement has no variables, just drop it.
651 if (!qs
.Subject
.IsVariable
&& !qs
.Predicate
.IsVariable
&& !qs
.Object
.IsVariable
)
657 private void InitSetStatement(Resource ent
, ref VarOrAnchor st
, Hashtable varIndex
) {
658 if (!varIndex
.ContainsKey(ent
)) {
659 st
.IsVariable
= false;
662 st
.IsVariable
= true;
663 st
.VarIndex
= (int)varIndex
[ent
];
667 private class QueryStatementComparer
: IComparer
{
671 public QueryStatementComparer(Hashtable setVars
, ResSet fps
, ResSet ifps
) {
672 this.setVars
= setVars
;
677 int IComparer
.Compare(object a
, object b
) {
678 return Compare((QueryStatement
)a
, (QueryStatement
)b
);
681 public int Compare(QueryStatement a
, QueryStatement b
) {
682 int optional
= a
.Optional
.CompareTo(b
.Optional
);
683 if (optional
!= 0) return optional
;
685 int numvars
= NumVars(a
).CompareTo(NumVars(b
));
686 if (numvars
!= 0) return numvars
;
688 int complexity
= Complexity(a
).CompareTo(Complexity(b
));
692 private int NumVars(QueryStatement s
) {
694 if (s
.Subject
.IsVariable
&& !setVars
.ContainsKey(s
.Subject
.VarIndex
))
696 if (s
.Predicate
.IsVariable
&& !setVars
.ContainsKey(s
.Predicate
.VarIndex
))
698 if (s
.Object
.IsVariable
&& !setVars
.ContainsKey(s
.Object
.VarIndex
))
703 private int Complexity(QueryStatement s
) {
704 if (s
.Predicate
.IsVariable
) return 2;
705 if ((!s
.Subject
.IsVariable
|| setVars
.ContainsKey(s
.Subject
.VarIndex
))
706 && fps
.Contains(s
.Predicate
.Anchor
))
708 if ((!s
.Object
.IsVariable
|| setVars
.ContainsKey(s
.Object
.VarIndex
))
709 && ifps
.Contains(s
.Predicate
.Anchor
))
715 private QueryStatement
[] InitBuildNode(ArrayList statements
, Hashtable setVars
) {
716 // Get the best statements to consider
717 // Because we can consider statements in groups, we need
719 QueryStatementComparer comparer
= new QueryStatementComparer(setVars
, fps
, ifps
);
720 ArrayList considerations
= new ArrayList();
721 for (int i
= 0; i
< statements
.Count
; i
++) {
722 QueryStatement next
= (QueryStatement
)statements
[i
];
724 if (considerations
.Count
> 0) {
725 QueryStatement curcomp
= (QueryStatement
) ((ArrayList
)considerations
[0])[0];
726 comp
= comparer
.Compare(curcomp
, next
);
729 if (comp
< 0) // next is worse than current
732 if (comp
> 0) // clear out worse possibilities
733 considerations
.Clear();
735 // next is equal to what we have. we can either
736 // group it with an existing statement, or make
737 // a new group out of it.
739 bool grouped
= false;
740 foreach (ArrayList g
in considerations
) {
741 // We can group next in g, if the only
742 // unset variables in next are the only
743 // unset variables in the statements in g.
744 // AND if nothing else is a variable,
745 // because of having multiply-bound variables
746 // with FindEntities. And none may be optional.
747 QueryStatement s
= (QueryStatement
)g
[0];
748 int su
= InitBuildNodeGetUniqueUnsetVar(s
, setVars
);
749 int nu
= InitBuildNodeGetUniqueUnsetVar(next
, setVars
);
750 if (su
== nu
&& su
!= -1 && s
.NumVars() == 1 && next
.NumVars() == 1
751 && !s
.Optional
&& !next
.Optional
) {
757 if (grouped
) continue;
759 // we didn't group it with anything existing,
760 // so make a new group
762 ArrayList
group = new ArrayList();
764 considerations
.Add(group);
767 // Pick the group with the most number of statements.
768 ArrayList bestgroup
= null;
769 foreach (ArrayList g
in considerations
) {
770 if (bestgroup
== null || bestgroup
.Count
< g
.Count
)
774 foreach (QueryStatement qs
in bestgroup
)
775 statements
.Remove(qs
);
777 return (QueryStatement
[])bestgroup
.ToArray(typeof(QueryStatement
));
780 int InitBuildNodeGetUniqueUnsetVar(QueryStatement s
, Hashtable setVars
) {
782 if (s
.Subject
.IsVariable
&& !setVars
.ContainsKey(s
.Subject
.VarIndex
))
783 ret
= s
.Subject
.VarIndex
;
784 if (s
.Predicate
.IsVariable
&& !setVars
.ContainsKey(s
.Predicate
.VarIndex
)) {
785 if (ret
!= -1) return -1;
786 ret
= s
.Predicate
.VarIndex
;
788 if (s
.Object
.IsVariable
&& !setVars
.ContainsKey(s
.Object
.VarIndex
)) {
789 if (ret
!= -1) return -1;
790 ret
= s
.Object
.VarIndex
;
796 public abstract class QueryResultSink
{
797 public virtual void Init(Entity
[] variables
) {
800 public abstract bool Add(VariableBinding
[] result
);
802 public virtual void Finished() {
806 public struct VariableBinding
{
810 internal VariableBinding(Entity variable
, Resource target
) {
815 public Entity Variable { get { return v; }
internal set { v = value; }
}
816 public Resource Target { get { return t; }
internal set { t = value; }
}