rdf/base/src/nsCompositeDataSource.cpp

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:a9d41af31888
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6 /*
7
8 A simple composite data source implementation. A composit data
9 source is just a strategy for combining individual data sources into
10 a collective graph.
11
12
13 1) A composite data source holds a sequence of data sources. The set
14 of data sources can be specified during creation of the
15 database. Data sources can also be added/deleted from a database
16 later.
17
18 2) The aggregation mechanism is based on simple super-positioning of
19 the graphs from the datasources. If there is a conflict (i.e.,
20 data source A has a true arc from foo to bar while data source B
21 has a false arc from foo to bar), the data source that it earlier
22 in the sequence wins.
23
24 The implementation below doesn't really do this and needs to be
25 fixed.
26
27 */
28
29 #include "xpcom-config.h"
30 #include "nsCOMPtr.h"
31 #include "nsIComponentManager.h"
32 #include "nsIRDFCompositeDataSource.h"
33 #include "nsIRDFNode.h"
34 #include "nsIRDFObserver.h"
35 #include "nsIRDFRemoteDataSource.h"
36 #include "nsTArray.h"
37 #include "nsCOMArray.h"
38 #include "nsArrayEnumerator.h"
39 #include "nsXPIDLString.h"
40 #include "rdf.h"
41 #include "nsCycleCollectionParticipant.h"
42
43 #include "nsEnumeratorUtils.h"
44
45 #ifdef PR_LOGGING
46 #include "prlog.h"
47 #include "prprf.h"
48 #include <stdio.h>
49 PRLogModuleInfo* nsRDFLog = nullptr;
50 #endif
51
52 //----------------------------------------------------------------------
53 //
54 // CompositeDataSourceImpl
55 //
56
57 class CompositeEnumeratorImpl;
58 class CompositeArcsInOutEnumeratorImpl;
59 class CompositeAssertionEnumeratorImpl;
60
61 class CompositeDataSourceImpl : public nsIRDFCompositeDataSource,
62 public nsIRDFObserver
63 {
64 public:
65 CompositeDataSourceImpl(void);
66 CompositeDataSourceImpl(char** dataSources);
67
68 // nsISupports interface
69 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
70 NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(CompositeDataSourceImpl,
71 nsIRDFCompositeDataSource)
72
73 // nsIRDFDataSource interface
74 NS_DECL_NSIRDFDATASOURCE
75
76 // nsIRDFCompositeDataSource interface
77 NS_DECL_NSIRDFCOMPOSITEDATASOURCE
78
79 // nsIRDFObserver interface
80 NS_DECL_NSIRDFOBSERVER
81
82 bool HasAssertionN(int n, nsIRDFResource* source,
83 nsIRDFResource* property,
84 nsIRDFNode* target,
85 bool tv);
86
87 protected:
88 nsCOMArray<nsIRDFObserver> mObservers;
89 nsCOMArray<nsIRDFDataSource> mDataSources;
90
91 bool mAllowNegativeAssertions;
92 bool mCoalesceDuplicateArcs;
93 int32_t mUpdateBatchNest;
94
95 virtual ~CompositeDataSourceImpl() {}
96
97 friend class CompositeEnumeratorImpl;
98 friend class CompositeArcsInOutEnumeratorImpl;
99 friend class CompositeAssertionEnumeratorImpl;
100 };
101
102 //----------------------------------------------------------------------
103 //
104 // CompositeEnumeratorImpl
105 //
106
107 class CompositeEnumeratorImpl : public nsISimpleEnumerator
108 {
109 // nsISupports
110 NS_DECL_ISUPPORTS
111
112 // nsISimpleEnumerator interface
113 NS_DECL_NSISIMPLEENUMERATOR
114
115 // pure abstract methods to be overridden
116 virtual nsresult
117 GetEnumerator(nsIRDFDataSource* aDataSource, nsISimpleEnumerator** aResult) = 0;
118
119 virtual nsresult
120 HasNegation(nsIRDFDataSource* aDataSource, nsIRDFNode* aNode, bool* aResult) = 0;
121
122 protected:
123 CompositeEnumeratorImpl(CompositeDataSourceImpl* aCompositeDataSource,
124 bool aAllowNegativeAssertions,
125 bool aCoalesceDuplicateArcs);
126
127 virtual ~CompositeEnumeratorImpl();
128
129 CompositeDataSourceImpl* mCompositeDataSource;
130
131 nsISimpleEnumerator* mCurrent;
132 nsIRDFNode* mResult;
133 int32_t mNext;
134 nsAutoTArray<nsCOMPtr<nsIRDFNode>, 8> mAlreadyReturned;
135 bool mAllowNegativeAssertions;
136 bool mCoalesceDuplicateArcs;
137 };
138
139
140 CompositeEnumeratorImpl::CompositeEnumeratorImpl(CompositeDataSourceImpl* aCompositeDataSource,
141 bool aAllowNegativeAssertions,
142 bool aCoalesceDuplicateArcs)
143 : mCompositeDataSource(aCompositeDataSource),
144 mCurrent(nullptr),
145 mResult(nullptr),
146 mNext(0),
147 mAllowNegativeAssertions(aAllowNegativeAssertions),
148 mCoalesceDuplicateArcs(aCoalesceDuplicateArcs)
149 {
150 NS_ADDREF(mCompositeDataSource);
151 }
152
153
154 CompositeEnumeratorImpl::~CompositeEnumeratorImpl(void)
155 {
156 NS_IF_RELEASE(mCurrent);
157 NS_IF_RELEASE(mResult);
158 NS_RELEASE(mCompositeDataSource);
159 }
160
161 NS_IMPL_ADDREF(CompositeEnumeratorImpl)
162 NS_IMPL_RELEASE(CompositeEnumeratorImpl)
163 NS_IMPL_QUERY_INTERFACE(CompositeEnumeratorImpl, nsISimpleEnumerator)
164
165 NS_IMETHODIMP
166 CompositeEnumeratorImpl::HasMoreElements(bool* aResult)
167 {
168 NS_PRECONDITION(aResult != nullptr, "null ptr");
169 if (! aResult)
170 return NS_ERROR_NULL_POINTER;
171
172 nsresult rv;
173
174 // If we've already queued up a next target, then yep, there are
175 // more elements.
176 if (mResult) {
177 *aResult = true;
178 return NS_OK;
179 }
180
181 // Otherwise, we'll need to find a next target, switching cursors
182 // if necessary.
183 for ( ; mNext < mCompositeDataSource->mDataSources.Count(); ++mNext) {
184 if (! mCurrent) {
185 // We don't have a current enumerator, so create a new one on
186 // the next data source.
187 nsIRDFDataSource* datasource =
188 mCompositeDataSource->mDataSources[mNext];
189
190 rv = GetEnumerator(datasource, &mCurrent);
191 if (NS_FAILED(rv)) return rv;
192 if (rv == NS_RDF_NO_VALUE)
193 continue;
194
195 NS_ASSERTION(mCurrent != nullptr, "you're always supposed to return an enumerator from GetEnumerator, punk.");
196 if (! mCurrent)
197 continue;
198 }
199
200 do {
201 int32_t i;
202
203 bool hasMore;
204 rv = mCurrent->HasMoreElements(&hasMore);
205 if (NS_FAILED(rv)) return rv;
206
207 // Is the current enumerator depleted?
208 if (! hasMore) {
209 NS_RELEASE(mCurrent);
210 break;
211 }
212
213 // Even if the current enumerator has more elements, we still
214 // need to check that the current element isn't masked by
215 // a negation in an earlier data source.
216
217 // "Peek" ahead and pull out the next target.
218 nsCOMPtr<nsISupports> result;
219 rv = mCurrent->GetNext(getter_AddRefs(result));
220 if (NS_FAILED(rv)) return rv;
221
222 rv = result->QueryInterface(NS_GET_IID(nsIRDFNode), (void**) &mResult);
223 if (NS_FAILED(rv)) return rv;
224
225 if (mAllowNegativeAssertions)
226 {
227 // See if any previous data source negates this
228 bool hasNegation = false;
229 for (i = mNext - 1; i >= 0; --i)
230 {
231 nsIRDFDataSource* datasource =
232 mCompositeDataSource->mDataSources[i];
233
234 rv = HasNegation(datasource, mResult, &hasNegation);
235 if (NS_FAILED(rv)) return rv;
236
237 if (hasNegation)
238 break;
239 }
240
241 // if so, we've gotta keep looking
242 if (hasNegation)
243 {
244 NS_RELEASE(mResult);
245 continue;
246 }
247 }
248
249 if (mCoalesceDuplicateArcs)
250 {
251 // Now see if we've returned it once already.
252 // XXX N.B. performance here...may want to hash if things get large?
253 bool alreadyReturned = false;
254 for (i = mAlreadyReturned.Length() - 1; i >= 0; --i)
255 {
256 if (mAlreadyReturned[i] == mResult)
257 {
258 alreadyReturned = true;
259 break;
260 }
261 }
262 if (alreadyReturned)
263 {
264 NS_RELEASE(mResult);
265 continue;
266 }
267 }
268
269 // If we get here, then we've really found one. It'll
270 // remain cached in mResult until GetNext() sucks it out.
271 *aResult = true;
272
273 // Remember that we returned it, so we don't return duplicates.
274
275 // XXX I wonder if we should make unique-checking be
276 // optional. This could get to be pretty expensive (this
277 // implementation turns iteration into O(n^2)).
278
279 if (mCoalesceDuplicateArcs)
280 {
281 mAlreadyReturned.AppendElement(mResult);
282 }
283
284 return NS_OK;
285 } while (1);
286 }
287
288 // if we get here, there aren't any elements left.
289 *aResult = false;
290 return NS_OK;
291 }
292
293
294 NS_IMETHODIMP
295 CompositeEnumeratorImpl::GetNext(nsISupports** aResult)
296 {
297 nsresult rv;
298
299 bool hasMore;
300 rv = HasMoreElements(&hasMore);
301 if (NS_FAILED(rv)) return rv;
302
303 if (! hasMore)
304 return NS_ERROR_UNEXPECTED;
305
306 // Don't AddRef: we "transfer" ownership to the caller
307 *aResult = mResult;
308 mResult = nullptr;
309
310 return NS_OK;
311 }
312
313 //----------------------------------------------------------------------
314 //
315 // CompositeArcsInOutEnumeratorImpl
316 //
317 //
318
319 class CompositeArcsInOutEnumeratorImpl : public CompositeEnumeratorImpl
320 {
321 public:
322 enum Type { eArcsIn, eArcsOut };
323
324 virtual ~CompositeArcsInOutEnumeratorImpl();
325
326 virtual nsresult
327 GetEnumerator(nsIRDFDataSource* aDataSource, nsISimpleEnumerator** aResult);
328
329 virtual nsresult
330 HasNegation(nsIRDFDataSource* aDataSource, nsIRDFNode* aNode, bool* aResult);
331
332 CompositeArcsInOutEnumeratorImpl(CompositeDataSourceImpl* aCompositeDataSource,
333 nsIRDFNode* aNode,
334 Type aType,
335 bool aAllowNegativeAssertions,
336 bool aCoalesceDuplicateArcs);
337
338 private:
339 nsIRDFNode* mNode;
340 Type mType;
341 };
342
343
344 CompositeArcsInOutEnumeratorImpl::CompositeArcsInOutEnumeratorImpl(
345 CompositeDataSourceImpl* aCompositeDataSource,
346 nsIRDFNode* aNode,
347 Type aType,
348 bool aAllowNegativeAssertions,
349 bool aCoalesceDuplicateArcs)
350 : CompositeEnumeratorImpl(aCompositeDataSource, aAllowNegativeAssertions, aCoalesceDuplicateArcs),
351 mNode(aNode),
352 mType(aType)
353 {
354 NS_ADDREF(mNode);
355 }
356
357 CompositeArcsInOutEnumeratorImpl::~CompositeArcsInOutEnumeratorImpl()
358 {
359 NS_RELEASE(mNode);
360 }
361
362
363 nsresult
364 CompositeArcsInOutEnumeratorImpl::GetEnumerator(
365 nsIRDFDataSource* aDataSource,
366 nsISimpleEnumerator** aResult)
367 {
368 if (mType == eArcsIn) {
369 return aDataSource->ArcLabelsIn(mNode, aResult);
370 }
371 else {
372 nsCOMPtr<nsIRDFResource> resource( do_QueryInterface(mNode) );
373 return aDataSource->ArcLabelsOut(resource, aResult);
374 }
375 }
376
377 nsresult
378 CompositeArcsInOutEnumeratorImpl::HasNegation(
379 nsIRDFDataSource* aDataSource,
380 nsIRDFNode* aNode,
381 bool* aResult)
382 {
383 *aResult = false;
384 return NS_OK;
385 }
386
387
388 //----------------------------------------------------------------------
389 //
390 // CompositeAssertionEnumeratorImpl
391 //
392
393 class CompositeAssertionEnumeratorImpl : public CompositeEnumeratorImpl
394 {
395 public:
396 virtual nsresult
397 GetEnumerator(nsIRDFDataSource* aDataSource, nsISimpleEnumerator** aResult);
398
399 virtual nsresult
400 HasNegation(nsIRDFDataSource* aDataSource, nsIRDFNode* aNode, bool* aResult);
401
402 CompositeAssertionEnumeratorImpl(CompositeDataSourceImpl* aCompositeDataSource,
403 nsIRDFResource* aSource,
404 nsIRDFResource* aProperty,
405 nsIRDFNode* aTarget,
406 bool aTruthValue,
407 bool aAllowNegativeAssertions,
408 bool aCoalesceDuplicateArcs);
409
410 virtual ~CompositeAssertionEnumeratorImpl();
411
412 private:
413 nsIRDFResource* mSource;
414 nsIRDFResource* mProperty;
415 nsIRDFNode* mTarget;
416 bool mTruthValue;
417 };
418
419
420 CompositeAssertionEnumeratorImpl::CompositeAssertionEnumeratorImpl(
421 CompositeDataSourceImpl* aCompositeDataSource,
422 nsIRDFResource* aSource,
423 nsIRDFResource* aProperty,
424 nsIRDFNode* aTarget,
425 bool aTruthValue,
426 bool aAllowNegativeAssertions,
427 bool aCoalesceDuplicateArcs)
428 : CompositeEnumeratorImpl(aCompositeDataSource, aAllowNegativeAssertions, aCoalesceDuplicateArcs),
429 mSource(aSource),
430 mProperty(aProperty),
431 mTarget(aTarget),
432 mTruthValue(aTruthValue)
433 {
434 NS_IF_ADDREF(mSource);
435 NS_ADDREF(mProperty); // always must be specified
436 NS_IF_ADDREF(mTarget);
437 }
438
439 CompositeAssertionEnumeratorImpl::~CompositeAssertionEnumeratorImpl()
440 {
441 NS_IF_RELEASE(mSource);
442 NS_RELEASE(mProperty);
443 NS_IF_RELEASE(mTarget);
444 }
445
446
447 nsresult
448 CompositeAssertionEnumeratorImpl::GetEnumerator(
449 nsIRDFDataSource* aDataSource,
450 nsISimpleEnumerator** aResult)
451 {
452 if (mSource) {
453 return aDataSource->GetTargets(mSource, mProperty, mTruthValue, aResult);
454 }
455 else {
456 return aDataSource->GetSources(mProperty, mTarget, mTruthValue, aResult);
457 }
458 }
459
460 nsresult
461 CompositeAssertionEnumeratorImpl::HasNegation(
462 nsIRDFDataSource* aDataSource,
463 nsIRDFNode* aNode,
464 bool* aResult)
465 {
466 if (mSource) {
467 return aDataSource->HasAssertion(mSource, mProperty, aNode, !mTruthValue, aResult);
468 }
469 else {
470 nsCOMPtr<nsIRDFResource> source( do_QueryInterface(aNode) );
471 return aDataSource->HasAssertion(source, mProperty, mTarget, !mTruthValue, aResult);
472 }
473 }
474
475 ////////////////////////////////////////////////////////////////////////
476
477 nsresult
478 NS_NewRDFCompositeDataSource(nsIRDFCompositeDataSource** result)
479 {
480 CompositeDataSourceImpl* db = new CompositeDataSourceImpl();
481 if (! db)
482 return NS_ERROR_OUT_OF_MEMORY;
483
484 *result = db;
485 NS_ADDREF(*result);
486 return NS_OK;
487 }
488
489
490 CompositeDataSourceImpl::CompositeDataSourceImpl(void)
491 : mAllowNegativeAssertions(true),
492 mCoalesceDuplicateArcs(true),
493 mUpdateBatchNest(0)
494 {
495 #ifdef PR_LOGGING
496 if (nsRDFLog == nullptr)
497 nsRDFLog = PR_NewLogModule("RDF");
498 #endif
499 }
500
501 //----------------------------------------------------------------------
502 //
503 // nsISupports interface
504 //
505
506 NS_IMPL_CYCLE_COLLECTION_CLASS(CompositeDataSourceImpl)
507
508 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(CompositeDataSourceImpl)
509 uint32_t i, count = tmp->mDataSources.Count();
510 for (i = count; i > 0; --i) {
511 tmp->mDataSources[i - 1]->RemoveObserver(tmp);
512 tmp->mDataSources.RemoveObjectAt(i - 1);
513 }
514 NS_IMPL_CYCLE_COLLECTION_UNLINK(mObservers);
515 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
516 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(CompositeDataSourceImpl)
517 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mObservers)
518 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDataSources)
519 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
520
521
522 NS_IMPL_CYCLE_COLLECTING_ADDREF(CompositeDataSourceImpl)
523 NS_IMPL_CYCLE_COLLECTING_RELEASE(CompositeDataSourceImpl)
524
525 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CompositeDataSourceImpl)
526 NS_INTERFACE_MAP_ENTRY(nsIRDFCompositeDataSource)
527 NS_INTERFACE_MAP_ENTRY(nsIRDFDataSource)
528 NS_INTERFACE_MAP_ENTRY(nsIRDFObserver)
529 NS_INTERFACE_MAP_ENTRY(nsIRDFCompositeDataSource)
530 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIRDFCompositeDataSource)
531 NS_INTERFACE_MAP_END
532
533
534 //----------------------------------------------------------------------
535 //
536 // nsIRDFDataSource interface
537 //
538
539 NS_IMETHODIMP
540 CompositeDataSourceImpl::GetURI(char* *uri)
541 {
542 *uri = nullptr;
543 return NS_OK;
544 }
545
546 NS_IMETHODIMP
547 CompositeDataSourceImpl::GetSource(nsIRDFResource* property,
548 nsIRDFNode* target,
549 bool tv,
550 nsIRDFResource** source)
551 {
552 if (!mAllowNegativeAssertions && !tv)
553 return(NS_RDF_NO_VALUE);
554
555 int32_t count = mDataSources.Count();
556 for (int32_t i = 0; i < count; ++i) {
557 nsresult rv;
558 rv = mDataSources[i]->GetSource(property, target, tv, source);
559 if (NS_FAILED(rv)) return rv;
560
561 if (rv == NS_RDF_NO_VALUE)
562 continue;
563
564 if (!mAllowNegativeAssertions) return(NS_OK);
565
566 // okay, found it. make sure we don't have the opposite
567 // asserted in a more local data source
568 if (!HasAssertionN(count-1, *source, property, target, !tv))
569 return NS_OK;
570
571 NS_RELEASE(*source);
572 return NS_RDF_NO_VALUE;
573 }
574 return NS_RDF_NO_VALUE;
575 }
576
577 NS_IMETHODIMP
578 CompositeDataSourceImpl::GetSources(nsIRDFResource* aProperty,
579 nsIRDFNode* aTarget,
580 bool aTruthValue,
581 nsISimpleEnumerator** aResult)
582 {
583 NS_PRECONDITION(aProperty != nullptr, "null ptr");
584 if (! aProperty)
585 return NS_ERROR_NULL_POINTER;
586
587 NS_PRECONDITION(aTarget != nullptr, "null ptr");
588 if (! aTarget)
589 return NS_ERROR_NULL_POINTER;
590
591 NS_PRECONDITION(aResult != nullptr, "null ptr");
592 if (! aResult)
593 return NS_ERROR_NULL_POINTER;
594
595 if (! mAllowNegativeAssertions && ! aTruthValue)
596 return(NS_RDF_NO_VALUE);
597
598 *aResult = new CompositeAssertionEnumeratorImpl(this, nullptr, aProperty,
599 aTarget, aTruthValue,
600 mAllowNegativeAssertions,
601 mCoalesceDuplicateArcs);
602
603 if (! *aResult)
604 return NS_ERROR_OUT_OF_MEMORY;
605
606 NS_ADDREF(*aResult);
607 return NS_OK;
608 }
609
610 NS_IMETHODIMP
611 CompositeDataSourceImpl::GetTarget(nsIRDFResource* aSource,
612 nsIRDFResource* aProperty,
613 bool aTruthValue,
614 nsIRDFNode** aResult)
615 {
616 NS_PRECONDITION(aSource != nullptr, "null ptr");
617 if (! aSource)
618 return NS_ERROR_NULL_POINTER;
619
620 NS_PRECONDITION(aProperty != nullptr, "null ptr");
621 if (! aProperty)
622 return NS_ERROR_NULL_POINTER;
623
624 NS_PRECONDITION(aResult != nullptr, "null ptr");
625 if (! aResult)
626 return NS_ERROR_NULL_POINTER;
627
628 if (! mAllowNegativeAssertions && ! aTruthValue)
629 return(NS_RDF_NO_VALUE);
630
631 int32_t count = mDataSources.Count();
632 for (int32_t i = 0; i < count; ++i) {
633 nsresult rv;
634 rv = mDataSources[i]->GetTarget(aSource, aProperty, aTruthValue,
635 aResult);
636 if (NS_FAILED(rv))
637 return rv;
638
639 if (rv == NS_OK) {
640 // okay, found it. make sure we don't have the opposite
641 // asserted in an earlier data source
642
643 if (mAllowNegativeAssertions) {
644 if (HasAssertionN(count-1, aSource, aProperty, *aResult, !aTruthValue)) {
645 // whoops, it's been negated.
646 NS_RELEASE(*aResult);
647 return NS_RDF_NO_VALUE;
648 }
649 }
650 return NS_OK;
651 }
652 }
653
654 // Otherwise, we couldn't find it at all.
655 return NS_RDF_NO_VALUE;
656 }
657
658 bool
659 CompositeDataSourceImpl::HasAssertionN(int n,
660 nsIRDFResource* aSource,
661 nsIRDFResource* aProperty,
662 nsIRDFNode* aTarget,
663 bool aTruthValue)
664 {
665 nsresult rv;
666 for (int32_t m = 0; m < n; ++m) {
667 bool result;
668 rv = mDataSources[m]->HasAssertion(aSource, aProperty, aTarget,
669 aTruthValue, &result);
670 if (NS_FAILED(rv))
671 return false;
672
673 // found it!
674 if (result)
675 return true;
676 }
677 return false;
678 }
679
680
681
682 NS_IMETHODIMP
683 CompositeDataSourceImpl::GetTargets(nsIRDFResource* aSource,
684 nsIRDFResource* aProperty,
685 bool aTruthValue,
686 nsISimpleEnumerator** aResult)
687 {
688 NS_PRECONDITION(aSource != nullptr, "null ptr");
689 if (! aSource)
690 return NS_ERROR_NULL_POINTER;
691
692 NS_PRECONDITION(aProperty != nullptr, "null ptr");
693 if (! aProperty)
694 return NS_ERROR_NULL_POINTER;
695
696 NS_PRECONDITION(aResult != nullptr, "null ptr");
697 if (! aResult)
698 return NS_ERROR_NULL_POINTER;
699
700 if (! mAllowNegativeAssertions && ! aTruthValue)
701 return(NS_RDF_NO_VALUE);
702
703 *aResult =
704 new CompositeAssertionEnumeratorImpl(this,
705 aSource, aProperty, nullptr,
706 aTruthValue,
707 mAllowNegativeAssertions,
708 mCoalesceDuplicateArcs);
709
710 if (! *aResult)
711 return NS_ERROR_OUT_OF_MEMORY;
712
713 NS_ADDREF(*aResult);
714 return NS_OK;
715 }
716
717 NS_IMETHODIMP
718 CompositeDataSourceImpl::Assert(nsIRDFResource* aSource,
719 nsIRDFResource* aProperty,
720 nsIRDFNode* aTarget,
721 bool aTruthValue)
722 {
723 NS_PRECONDITION(aSource != nullptr, "null ptr");
724 if (! aSource)
725 return NS_ERROR_NULL_POINTER;
726
727 NS_PRECONDITION(aProperty != nullptr, "null ptr");
728 if (! aProperty)
729 return NS_ERROR_NULL_POINTER;
730
731 NS_PRECONDITION(aTarget != nullptr, "null ptr");
732 if (! aTarget)
733 return NS_ERROR_NULL_POINTER;
734
735 if (! mAllowNegativeAssertions && ! aTruthValue)
736 return(NS_RDF_ASSERTION_REJECTED);
737
738 nsresult rv;
739
740 // XXX Need to add back the stuff for unblocking ...
741
742 // We iterate backwards from the last data source which was added
743 // ("the most remote") to the first ("the most local"), trying to
744 // apply the assertion in each.
745 for (int32_t i = mDataSources.Count() - 1; i >= 0; --i) {
746 rv = mDataSources[i]->Assert(aSource, aProperty, aTarget, aTruthValue);
747 if (NS_RDF_ASSERTION_ACCEPTED == rv)
748 return rv;
749
750 if (NS_FAILED(rv))
751 return rv;
752 }
753
754 // nobody wanted to accept it
755 return NS_RDF_ASSERTION_REJECTED;
756 }
757
758 NS_IMETHODIMP
759 CompositeDataSourceImpl::Unassert(nsIRDFResource* aSource,
760 nsIRDFResource* aProperty,
761 nsIRDFNode* aTarget)
762 {
763 NS_PRECONDITION(aSource != nullptr, "null ptr");
764 if (! aSource)
765 return NS_ERROR_NULL_POINTER;
766
767 NS_PRECONDITION(aProperty != nullptr, "null ptr");
768 if (! aProperty)
769 return NS_ERROR_NULL_POINTER;
770
771 NS_PRECONDITION(aTarget != nullptr, "null ptr");
772 if (! aTarget)
773 return NS_ERROR_NULL_POINTER;
774
775 nsresult rv;
776
777 // Iterate through each of the datasources, starting with "the
778 // most local" and moving to "the most remote". If _any_ of the
779 // datasources have the assertion, attempt to unassert it.
780 bool unasserted = true;
781 int32_t i;
782 int32_t count = mDataSources.Count();
783 for (i = 0; i < count; ++i) {
784 nsIRDFDataSource* ds = mDataSources[i];
785
786 bool hasAssertion;
787 rv = ds->HasAssertion(aSource, aProperty, aTarget, true, &hasAssertion);
788 if (NS_FAILED(rv)) return rv;
789
790 if (hasAssertion) {
791 rv = ds->Unassert(aSource, aProperty, aTarget);
792 if (NS_FAILED(rv)) return rv;
793
794 if (rv != NS_RDF_ASSERTION_ACCEPTED) {
795 unasserted = false;
796 break;
797 }
798 }
799 }
800
801 // Either none of the datasources had it, or they were all willing
802 // to let it be unasserted.
803 if (unasserted)
804 return NS_RDF_ASSERTION_ACCEPTED;
805
806 // If we get here, one of the datasources already had the
807 // assertion, and was adamant about not letting us remove
808 // it. Iterate from the "most local" to the "most remote"
809 // attempting to assert the negation...
810 for (i = 0; i < count; ++i) {
811 rv = mDataSources[i]->Assert(aSource, aProperty, aTarget, false);
812 if (NS_FAILED(rv)) return rv;
813
814 // Did it take?
815 if (rv == NS_RDF_ASSERTION_ACCEPTED)
816 return rv;
817 }
818
819 // Couln't get anyone to accept the negation, either.
820 return NS_RDF_ASSERTION_REJECTED;
821 }
822
823 NS_IMETHODIMP
824 CompositeDataSourceImpl::Change(nsIRDFResource* aSource,
825 nsIRDFResource* aProperty,
826 nsIRDFNode* aOldTarget,
827 nsIRDFNode* aNewTarget)
828 {
829 NS_PRECONDITION(aSource != nullptr, "null ptr");
830 if (! aSource)
831 return NS_ERROR_NULL_POINTER;
832
833 NS_PRECONDITION(aProperty != nullptr, "null ptr");
834 if (! aProperty)
835 return NS_ERROR_NULL_POINTER;
836
837 NS_PRECONDITION(aOldTarget != nullptr, "null ptr");
838 if (! aOldTarget)
839 return NS_ERROR_NULL_POINTER;
840
841 NS_PRECONDITION(aNewTarget != nullptr, "null ptr");
842 if (! aNewTarget)
843 return NS_ERROR_NULL_POINTER;
844
845 nsresult rv;
846
847 // XXX So we're assuming that a datasource _must_ accept the
848 // atomic change; i.e., we can't split it up across two
849 // datasources. That sucks.
850
851 // We iterate backwards from the last data source which was added
852 // ("the most remote") to the first ("the most local"), trying to
853 // apply the change in each.
854 for (int32_t i = mDataSources.Count() - 1; i >= 0; --i) {
855 rv = mDataSources[i]->Change(aSource, aProperty, aOldTarget, aNewTarget);
856 if (NS_RDF_ASSERTION_ACCEPTED == rv)
857 return rv;
858
859 if (NS_FAILED(rv))
860 return rv;
861 }
862
863 // nobody wanted to accept it
864 return NS_RDF_ASSERTION_REJECTED;
865 }
866
867 NS_IMETHODIMP
868 CompositeDataSourceImpl::Move(nsIRDFResource* aOldSource,
869 nsIRDFResource* aNewSource,
870 nsIRDFResource* aProperty,
871 nsIRDFNode* aTarget)
872 {
873 NS_PRECONDITION(aOldSource != nullptr, "null ptr");
874 if (! aOldSource)
875 return NS_ERROR_NULL_POINTER;
876
877 NS_PRECONDITION(aNewSource != nullptr, "null ptr");
878 if (! aNewSource)
879 return NS_ERROR_NULL_POINTER;
880
881 NS_PRECONDITION(aProperty != nullptr, "null ptr");
882 if (! aProperty)
883 return NS_ERROR_NULL_POINTER;
884
885 NS_PRECONDITION(aTarget != nullptr, "null ptr");
886 if (! aTarget)
887 return NS_ERROR_NULL_POINTER;
888
889 nsresult rv;
890
891 // XXX So we're assuming that a datasource _must_ accept the
892 // atomic move; i.e., we can't split it up across two
893 // datasources. That sucks.
894
895 // We iterate backwards from the last data source which was added
896 // ("the most remote") to the first ("the most local"), trying to
897 // apply the assertion in each.
898 for (int32_t i = mDataSources.Count() - 1; i >= 0; --i) {
899 rv = mDataSources[i]->Move(aOldSource, aNewSource, aProperty, aTarget);
900 if (NS_RDF_ASSERTION_ACCEPTED == rv)
901 return rv;
902
903 if (NS_FAILED(rv))
904 return rv;
905 }
906
907 // nobody wanted to accept it
908 return NS_RDF_ASSERTION_REJECTED;
909 }
910
911
912 NS_IMETHODIMP
913 CompositeDataSourceImpl::HasAssertion(nsIRDFResource* aSource,
914 nsIRDFResource* aProperty,
915 nsIRDFNode* aTarget,
916 bool aTruthValue,
917 bool* aResult)
918 {
919 NS_PRECONDITION(aSource != nullptr, "null ptr");
920 if (! aSource)
921 return NS_ERROR_NULL_POINTER;
922
923 NS_PRECONDITION(aProperty != nullptr, "null ptr");
924 if (! aProperty)
925 return NS_ERROR_NULL_POINTER;
926
927 NS_PRECONDITION(aResult != nullptr, "null ptr");
928 if (! aResult)
929 return NS_ERROR_NULL_POINTER;
930
931 if (! mAllowNegativeAssertions && ! aTruthValue)
932 {
933 *aResult = false;
934 return(NS_OK);
935 }
936
937 nsresult rv;
938
939 // Otherwise, look through all the data sources to see if anyone
940 // has the positive...
941 int32_t count = mDataSources.Count();
942 for (int32_t i = 0; i < count; ++i) {
943 nsIRDFDataSource* datasource = mDataSources[i];
944 rv = datasource->HasAssertion(aSource, aProperty, aTarget, aTruthValue, aResult);
945 if (NS_FAILED(rv)) return rv;
946
947 if (*aResult)
948 return NS_OK;
949
950 if (mAllowNegativeAssertions)
951 {
952 bool hasNegation;
953 rv = datasource->HasAssertion(aSource, aProperty, aTarget, !aTruthValue, &hasNegation);
954 if (NS_FAILED(rv)) return rv;
955
956 if (hasNegation)
957 {
958 *aResult = false;
959 return NS_OK;
960 }
961 }
962 }
963
964 // If we get here, nobody had the assertion at all
965 *aResult = false;
966 return NS_OK;
967 }
968
969 NS_IMETHODIMP
970 CompositeDataSourceImpl::AddObserver(nsIRDFObserver* aObserver)
971 {
972 NS_PRECONDITION(aObserver != nullptr, "null ptr");
973 if (! aObserver)
974 return NS_ERROR_NULL_POINTER;
975
976 // XXX ensure uniqueness?
977 mObservers.AppendObject(aObserver);
978
979 return NS_OK;
980 }
981
982 NS_IMETHODIMP
983 CompositeDataSourceImpl::RemoveObserver(nsIRDFObserver* aObserver)
984 {
985 NS_PRECONDITION(aObserver != nullptr, "null ptr");
986 if (! aObserver)
987 return NS_ERROR_NULL_POINTER;
988
989 mObservers.RemoveObject(aObserver);
990
991 return NS_OK;
992 }
993
994 NS_IMETHODIMP
995 CompositeDataSourceImpl::HasArcIn(nsIRDFNode *aNode, nsIRDFResource *aArc, bool *result)
996 {
997 nsresult rv;
998 *result = false;
999 int32_t count = mDataSources.Count();
1000 for (int32_t i = 0; i < count; ++i) {
1001 rv = mDataSources[i]->HasArcIn(aNode, aArc, result);
1002 if (NS_FAILED(rv)) return rv;
1003 if (*result)
1004 return NS_OK;
1005 }
1006 return NS_OK;
1007 }
1008
1009 NS_IMETHODIMP
1010 CompositeDataSourceImpl::HasArcOut(nsIRDFResource *aSource, nsIRDFResource *aArc, bool *result)
1011 {
1012 nsresult rv;
1013 *result = false;
1014 int32_t count = mDataSources.Count();
1015 for (int32_t i = 0; i < count; ++i) {
1016 rv = mDataSources[i]->HasArcOut(aSource, aArc, result);
1017 if (NS_FAILED(rv)) return rv;
1018 if (*result)
1019 return NS_OK;
1020 }
1021 return NS_OK;
1022 }
1023
1024 NS_IMETHODIMP
1025 CompositeDataSourceImpl::ArcLabelsIn(nsIRDFNode* aTarget, nsISimpleEnumerator** aResult)
1026 {
1027 NS_PRECONDITION(aTarget != nullptr, "null ptr");
1028 if (! aTarget)
1029 return NS_ERROR_NULL_POINTER;
1030
1031 NS_PRECONDITION(aResult != nullptr, "null ptr");
1032 if (! aResult)
1033 return NS_ERROR_NULL_POINTER;
1034
1035 nsISimpleEnumerator* result =
1036 new CompositeArcsInOutEnumeratorImpl(this, aTarget,
1037 CompositeArcsInOutEnumeratorImpl::eArcsIn,
1038 mAllowNegativeAssertions,
1039 mCoalesceDuplicateArcs);
1040
1041 if (! result)
1042 return NS_ERROR_OUT_OF_MEMORY;
1043
1044 NS_ADDREF(result);
1045 *aResult = result;
1046 return NS_OK;
1047 }
1048
1049 NS_IMETHODIMP
1050 CompositeDataSourceImpl::ArcLabelsOut(nsIRDFResource* aSource,
1051 nsISimpleEnumerator** aResult)
1052 {
1053 NS_PRECONDITION(aSource != nullptr, "null ptr");
1054 if (! aSource)
1055 return NS_ERROR_NULL_POINTER;
1056
1057 NS_PRECONDITION(aResult != nullptr, "null ptr");
1058 if (! aResult)
1059 return NS_ERROR_NULL_POINTER;
1060
1061 nsISimpleEnumerator* result =
1062 new CompositeArcsInOutEnumeratorImpl(this, aSource,
1063 CompositeArcsInOutEnumeratorImpl::eArcsOut,
1064 mAllowNegativeAssertions,
1065 mCoalesceDuplicateArcs);
1066
1067 if (! result)
1068 return NS_ERROR_OUT_OF_MEMORY;
1069
1070 NS_ADDREF(result);
1071 *aResult = result;
1072 return NS_OK;
1073 }
1074
1075 NS_IMETHODIMP
1076 CompositeDataSourceImpl::GetAllResources(nsISimpleEnumerator** aResult)
1077 {
1078 NS_NOTYETIMPLEMENTED("CompositeDataSourceImpl::GetAllResources");
1079 return NS_ERROR_NOT_IMPLEMENTED;
1080 }
1081
1082 NS_IMETHODIMP
1083 CompositeDataSourceImpl::GetAllCmds(nsIRDFResource* source,
1084 nsISimpleEnumerator/*<nsIRDFResource>*/** result)
1085 {
1086 nsresult rv;
1087 nsCOMPtr<nsISimpleEnumerator> set;
1088
1089 for (int32_t i = 0; i < mDataSources.Count(); i++)
1090 {
1091 nsCOMPtr<nsISimpleEnumerator> dsCmds;
1092
1093 rv = mDataSources[i]->GetAllCmds(source, getter_AddRefs(dsCmds));
1094 if (NS_SUCCEEDED(rv))
1095 {
1096 nsCOMPtr<nsISimpleEnumerator> tmp;
1097 rv = NS_NewUnionEnumerator(getter_AddRefs(tmp), set, dsCmds);
1098 set.swap(tmp);
1099 if (NS_FAILED(rv)) return(rv);
1100 }
1101 }
1102
1103 set.forget(result);
1104 return NS_OK;
1105 }
1106
1107 NS_IMETHODIMP
1108 CompositeDataSourceImpl::IsCommandEnabled(nsISupportsArray/*<nsIRDFResource>*/* aSources,
1109 nsIRDFResource* aCommand,
1110 nsISupportsArray/*<nsIRDFResource>*/* aArguments,
1111 bool* aResult)
1112 {
1113 nsresult rv;
1114 for (int32_t i = mDataSources.Count() - 1; i >= 0; --i) {
1115 bool enabled = true;
1116 rv = mDataSources[i]->IsCommandEnabled(aSources, aCommand, aArguments, &enabled);
1117 if (NS_FAILED(rv) && (rv != NS_ERROR_NOT_IMPLEMENTED))
1118 {
1119 return(rv);
1120 }
1121
1122 if (! enabled) {
1123 *aResult = false;
1124 return(NS_OK);
1125 }
1126 }
1127 *aResult = true;
1128 return(NS_OK);
1129 }
1130
1131 NS_IMETHODIMP
1132 CompositeDataSourceImpl::DoCommand(nsISupportsArray/*<nsIRDFResource>*/* aSources,
1133 nsIRDFResource* aCommand,
1134 nsISupportsArray/*<nsIRDFResource>*/* aArguments)
1135 {
1136 for (int32_t i = mDataSources.Count() - 1; i >= 0; --i) {
1137 nsresult rv = mDataSources[i]->DoCommand(aSources, aCommand, aArguments);
1138 if (NS_FAILED(rv) && (rv != NS_ERROR_NOT_IMPLEMENTED))
1139 {
1140 return(rv); // all datasources must succeed
1141 }
1142 }
1143 return(NS_OK);
1144 }
1145
1146 NS_IMETHODIMP
1147 CompositeDataSourceImpl::BeginUpdateBatch()
1148 {
1149 for (int32_t i = mDataSources.Count() - 1; i >= 0; --i) {
1150 mDataSources[i]->BeginUpdateBatch();
1151 }
1152 return NS_OK;
1153 }
1154
1155 NS_IMETHODIMP
1156 CompositeDataSourceImpl::EndUpdateBatch()
1157 {
1158 for (int32_t i = mDataSources.Count() - 1; i >= 0; --i) {
1159 mDataSources[i]->EndUpdateBatch();
1160 }
1161 return NS_OK;
1162 }
1163
1164 ////////////////////////////////////////////////////////////////////////
1165 // nsIRDFCompositeDataSource methods
1166 // XXX rvg We should make this take an additional argument specifying where
1167 // in the sequence of data sources (of the db), the new data source should
1168 // fit in. Right now, the new datasource gets stuck at the end.
1169 // need to add the observers of the CompositeDataSourceImpl to the new data source.
1170
1171 NS_IMETHODIMP
1172 CompositeDataSourceImpl::GetAllowNegativeAssertions(bool *aAllowNegativeAssertions)
1173 {
1174 *aAllowNegativeAssertions = mAllowNegativeAssertions;
1175 return(NS_OK);
1176 }
1177
1178 NS_IMETHODIMP
1179 CompositeDataSourceImpl::SetAllowNegativeAssertions(bool aAllowNegativeAssertions)
1180 {
1181 mAllowNegativeAssertions = aAllowNegativeAssertions;
1182 return(NS_OK);
1183 }
1184
1185 NS_IMETHODIMP
1186 CompositeDataSourceImpl::GetCoalesceDuplicateArcs(bool *aCoalesceDuplicateArcs)
1187 {
1188 *aCoalesceDuplicateArcs = mCoalesceDuplicateArcs;
1189 return(NS_OK);
1190 }
1191
1192 NS_IMETHODIMP
1193 CompositeDataSourceImpl::SetCoalesceDuplicateArcs(bool aCoalesceDuplicateArcs)
1194 {
1195 mCoalesceDuplicateArcs = aCoalesceDuplicateArcs;
1196 return(NS_OK);
1197 }
1198
1199 NS_IMETHODIMP
1200 CompositeDataSourceImpl::AddDataSource(nsIRDFDataSource* aDataSource)
1201 {
1202 NS_ASSERTION(aDataSource != nullptr, "null ptr");
1203 if (! aDataSource)
1204 return NS_ERROR_NULL_POINTER;
1205
1206 mDataSources.AppendObject(aDataSource);
1207 aDataSource->AddObserver(this);
1208 return NS_OK;
1209 }
1210
1211
1212
1213 NS_IMETHODIMP
1214 CompositeDataSourceImpl::RemoveDataSource(nsIRDFDataSource* aDataSource)
1215 {
1216 NS_ASSERTION(aDataSource != nullptr, "null ptr");
1217 if (! aDataSource)
1218 return NS_ERROR_NULL_POINTER;
1219
1220
1221 if (mDataSources.IndexOf(aDataSource) >= 0) {
1222 aDataSource->RemoveObserver(this);
1223 mDataSources.RemoveObject(aDataSource);
1224 }
1225 return NS_OK;
1226 }
1227
1228
1229 NS_IMETHODIMP
1230 CompositeDataSourceImpl::GetDataSources(nsISimpleEnumerator** _result)
1231 {
1232 // NS_NewArrayEnumerator for an nsCOMArray takes a snapshot of the
1233 // current state.
1234 return NS_NewArrayEnumerator(_result, mDataSources);
1235 }
1236
1237 NS_IMETHODIMP
1238 CompositeDataSourceImpl::OnAssert(nsIRDFDataSource* aDataSource,
1239 nsIRDFResource* aSource,
1240 nsIRDFResource* aProperty,
1241 nsIRDFNode* aTarget)
1242 {
1243 // Make sure that the assertion isn't masked by another
1244 // datasource.
1245 //
1246 // XXX We could make this more efficient if we knew _which_
1247 // datasource actually served up the OnAssert(): we could use
1248 // HasAssertionN() to only search datasources _before_ the
1249 // datasource that coughed up the assertion.
1250 nsresult rv = NS_OK;
1251
1252 if (mAllowNegativeAssertions)
1253 {
1254 bool hasAssertion;
1255 rv = HasAssertion(aSource, aProperty, aTarget, true, &hasAssertion);
1256 if (NS_FAILED(rv)) return rv;
1257
1258 if (! hasAssertion)
1259 return(NS_OK);
1260 }
1261
1262 for (int32_t i = mObservers.Count() - 1; i >= 0; --i) {
1263 mObservers[i]->OnAssert(this, aSource, aProperty, aTarget);
1264 }
1265 return NS_OK;
1266 }
1267
1268 NS_IMETHODIMP
1269 CompositeDataSourceImpl::OnUnassert(nsIRDFDataSource* aDataSource,
1270 nsIRDFResource* aSource,
1271 nsIRDFResource* aProperty,
1272 nsIRDFNode* aTarget)
1273 {
1274 // Make sure that the un-assertion doesn't just unmask the
1275 // same assertion in a different datasource.
1276 //
1277 // XXX We could make this more efficient if we knew _which_
1278 // datasource actually served up the OnAssert(): we could use
1279 // HasAssertionN() to only search datasources _before_ the
1280 // datasource that coughed up the assertion.
1281 nsresult rv;
1282
1283 if (mAllowNegativeAssertions)
1284 {
1285 bool hasAssertion;
1286 rv = HasAssertion(aSource, aProperty, aTarget, true, &hasAssertion);
1287 if (NS_FAILED(rv)) return rv;
1288
1289 if (hasAssertion)
1290 return NS_OK;
1291 }
1292
1293 for (int32_t i = mObservers.Count() - 1; i >= 0; --i) {
1294 mObservers[i]->OnUnassert(this, aSource, aProperty, aTarget);
1295 }
1296 return NS_OK;
1297 }
1298
1299
1300 NS_IMETHODIMP
1301 CompositeDataSourceImpl::OnChange(nsIRDFDataSource* aDataSource,
1302 nsIRDFResource* aSource,
1303 nsIRDFResource* aProperty,
1304 nsIRDFNode* aOldTarget,
1305 nsIRDFNode* aNewTarget)
1306 {
1307 // Make sure that the change is actually visible, and not hidden
1308 // by an assertion in a different datasource.
1309 //
1310 // XXX Because of aggregation, this could actually mutate into a
1311 // variety of OnAssert or OnChange notifications, which we'll
1312 // ignore for now :-/.
1313 for (int32_t i = mObservers.Count() - 1; i >= 0; --i) {
1314 mObservers[i]->OnChange(this, aSource, aProperty,
1315 aOldTarget, aNewTarget);
1316 }
1317 return NS_OK;
1318 }
1319
1320
1321 NS_IMETHODIMP
1322 CompositeDataSourceImpl::OnMove(nsIRDFDataSource* aDataSource,
1323 nsIRDFResource* aOldSource,
1324 nsIRDFResource* aNewSource,
1325 nsIRDFResource* aProperty,
1326 nsIRDFNode* aTarget)
1327 {
1328 // Make sure that the move is actually visible, and not hidden
1329 // by an assertion in a different datasource.
1330 //
1331 // XXX Because of aggregation, this could actually mutate into a
1332 // variety of OnAssert or OnMove notifications, which we'll
1333 // ignore for now :-/.
1334 for (int32_t i = mObservers.Count() - 1; i >= 0; --i) {
1335 mObservers[i]->OnMove(this, aOldSource, aNewSource,
1336 aProperty, aTarget);
1337 }
1338 return NS_OK;
1339 }
1340
1341
1342 NS_IMETHODIMP
1343 CompositeDataSourceImpl::OnBeginUpdateBatch(nsIRDFDataSource* aDataSource)
1344 {
1345 if (mUpdateBatchNest++ == 0) {
1346 for (int32_t i = mObservers.Count() - 1; i >= 0; --i) {
1347 mObservers[i]->OnBeginUpdateBatch(this);
1348 }
1349 }
1350 return NS_OK;
1351 }
1352
1353
1354 NS_IMETHODIMP
1355 CompositeDataSourceImpl::OnEndUpdateBatch(nsIRDFDataSource* aDataSource)
1356 {
1357 NS_ASSERTION(mUpdateBatchNest > 0, "badly nested update batch");
1358 if (--mUpdateBatchNest == 0) {
1359 for (int32_t i = mObservers.Count() - 1; i >= 0; --i) {
1360 mObservers[i]->OnEndUpdateBatch(this);
1361 }
1362 }
1363 return NS_OK;
1364 }

mercurial