rdf/base/src/nsRDFContainer.cpp

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:aa8d77d9efd2
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 Implementation for the RDF container.
9
10 Notes
11 -----
12
13 1. RDF containers are one-indexed. This means that a lot of the loops
14 that you'd normally think you'd write like this:
15
16 for (i = 0; i < count; ++i) {}
17
18 You've gotta write like this:
19
20 for (i = 1; i <= count; ++i) {}
21
22 "Sure, right, yeah, of course.", you say. Well maybe I'm just
23 thick, but it's easy to slip up.
24
25 2. The RDF:nextVal property on the container is an
26 implementation-level hack that is used to quickly compute the
27 next value for appending to the container. It will no doubt
28 become royally screwed up in the case of aggregation.
29
30 3. The RDF:nextVal property is also used to retrieve the count of
31 elements in the container.
32
33 */
34
35
36 #include "nsCOMPtr.h"
37 #include "nsIRDFContainer.h"
38 #include "nsIRDFContainerUtils.h"
39 #include "nsIRDFInMemoryDataSource.h"
40 #include "nsIRDFPropagatableDataSource.h"
41 #include "nsIRDFService.h"
42 #include "nsIServiceManager.h"
43 #include "nsRDFCID.h"
44 #include "nsString.h"
45 #include "nsXPIDLString.h"
46 #include "rdf.h"
47
48 #define RDF_SEQ_LIST_LIMIT 8
49
50 class RDFContainerImpl : public nsIRDFContainer
51 {
52 public:
53
54 // nsISupports interface
55 NS_DECL_ISUPPORTS
56
57 // nsIRDFContainer interface
58 NS_DECL_NSIRDFCONTAINER
59
60 private:
61 friend nsresult NS_NewRDFContainer(nsIRDFContainer** aResult);
62
63 RDFContainerImpl();
64 virtual ~RDFContainerImpl();
65
66 nsresult Init();
67
68 nsresult Renumber(int32_t aStartIndex, int32_t aIncrement);
69 nsresult SetNextValue(int32_t aIndex);
70 nsresult GetNextValue(nsIRDFResource** aResult);
71
72 nsIRDFDataSource* mDataSource;
73 nsIRDFResource* mContainer;
74
75 // pseudo constants
76 static int32_t gRefCnt;
77 static nsIRDFService* gRDFService;
78 static nsIRDFContainerUtils* gRDFContainerUtils;
79 static nsIRDFResource* kRDF_nextVal;
80 };
81
82
83 int32_t RDFContainerImpl::gRefCnt = 0;
84 nsIRDFService* RDFContainerImpl::gRDFService;
85 nsIRDFContainerUtils* RDFContainerImpl::gRDFContainerUtils;
86 nsIRDFResource* RDFContainerImpl::kRDF_nextVal;
87
88 ////////////////////////////////////////////////////////////////////////
89 // nsISupports interface
90
91 NS_IMPL_ISUPPORTS(RDFContainerImpl, nsIRDFContainer)
92
93
94
95 ////////////////////////////////////////////////////////////////////////
96 // nsIRDFContainer interface
97
98 NS_IMETHODIMP
99 RDFContainerImpl::GetDataSource(nsIRDFDataSource** _retval)
100 {
101 *_retval = mDataSource;
102 NS_IF_ADDREF(*_retval);
103 return NS_OK;
104 }
105
106
107 NS_IMETHODIMP
108 RDFContainerImpl::GetResource(nsIRDFResource** _retval)
109 {
110 *_retval = mContainer;
111 NS_IF_ADDREF(*_retval);
112 return NS_OK;
113 }
114
115
116 NS_IMETHODIMP
117 RDFContainerImpl::Init(nsIRDFDataSource *aDataSource, nsIRDFResource *aContainer)
118 {
119 NS_PRECONDITION(aDataSource != nullptr, "null ptr");
120 if (! aDataSource)
121 return NS_ERROR_NULL_POINTER;
122
123 NS_PRECONDITION(aContainer != nullptr, "null ptr");
124 if (! aContainer)
125 return NS_ERROR_NULL_POINTER;
126
127 nsresult rv;
128 bool isContainer;
129 rv = gRDFContainerUtils->IsContainer(aDataSource, aContainer, &isContainer);
130 if (NS_FAILED(rv)) return rv;
131
132 // ``throw'' if we can't create a container on the specified
133 // datasource/resource combination.
134 if (! isContainer)
135 return NS_ERROR_FAILURE;
136
137 NS_IF_RELEASE(mDataSource);
138 mDataSource = aDataSource;
139 NS_ADDREF(mDataSource);
140
141 NS_IF_RELEASE(mContainer);
142 mContainer = aContainer;
143 NS_ADDREF(mContainer);
144
145 return NS_OK;
146 }
147
148
149 NS_IMETHODIMP
150 RDFContainerImpl::GetCount(int32_t *aCount)
151 {
152 if (!mDataSource || !mContainer)
153 return NS_ERROR_NOT_INITIALIZED;
154
155 nsresult rv;
156
157 // Get the next value, which hangs off of the bag via the
158 // RDF:nextVal property. This is the _next value_ that will get
159 // assigned in a one-indexed array. So, it's actually _one more_
160 // than the actual count of elements in the container.
161 //
162 // XXX To handle aggregation, this should probably be a
163 // GetTargets() that enumerates all of the values and picks the
164 // largest one.
165 nsCOMPtr<nsIRDFNode> nextValNode;
166 rv = mDataSource->GetTarget(mContainer, kRDF_nextVal, true, getter_AddRefs(nextValNode));
167 if (NS_FAILED(rv)) return rv;
168
169 if (rv == NS_RDF_NO_VALUE)
170 return NS_ERROR_UNEXPECTED;
171
172 nsCOMPtr<nsIRDFLiteral> nextValLiteral;
173 rv = nextValNode->QueryInterface(NS_GET_IID(nsIRDFLiteral), getter_AddRefs(nextValLiteral));
174 if (NS_FAILED(rv)) return rv;
175
176 const char16_t *s;
177 rv = nextValLiteral->GetValueConst( &s );
178 if (NS_FAILED(rv)) return rv;
179
180 nsAutoString nextValStr(s);
181
182 int32_t nextVal;
183 nsresult err;
184 nextVal = nextValStr.ToInteger(&err);
185 if (NS_FAILED(err))
186 return NS_ERROR_UNEXPECTED;
187
188 *aCount = nextVal - 1;
189 return NS_OK;
190 }
191
192
193 NS_IMETHODIMP
194 RDFContainerImpl::GetElements(nsISimpleEnumerator **_retval)
195 {
196 if (!mDataSource || !mContainer)
197 return NS_ERROR_NOT_INITIALIZED;
198
199 return NS_NewContainerEnumerator(mDataSource, mContainer, _retval);
200 }
201
202
203 NS_IMETHODIMP
204 RDFContainerImpl::AppendElement(nsIRDFNode *aElement)
205 {
206 if (!mDataSource || !mContainer)
207 return NS_ERROR_NOT_INITIALIZED;
208
209 NS_PRECONDITION(aElement != nullptr, "null ptr");
210 if (! aElement)
211 return NS_ERROR_NULL_POINTER;
212
213 nsresult rv;
214
215 nsCOMPtr<nsIRDFResource> nextVal;
216 rv = GetNextValue(getter_AddRefs(nextVal));
217 if (NS_FAILED(rv)) return rv;
218
219 rv = mDataSource->Assert(mContainer, nextVal, aElement, true);
220 if (NS_FAILED(rv)) return rv;
221
222 return NS_OK;
223 }
224
225
226 NS_IMETHODIMP
227 RDFContainerImpl::RemoveElement(nsIRDFNode *aElement, bool aRenumber)
228 {
229 if (!mDataSource || !mContainer)
230 return NS_ERROR_NOT_INITIALIZED;
231
232 NS_PRECONDITION(aElement != nullptr, "null ptr");
233 if (! aElement)
234 return NS_ERROR_NULL_POINTER;
235
236 nsresult rv;
237
238 int32_t idx;
239 rv = IndexOf(aElement, &idx);
240 if (NS_FAILED(rv)) return rv;
241
242 if (idx < 0)
243 return NS_OK;
244
245 // Remove the element.
246 nsCOMPtr<nsIRDFResource> ordinal;
247 rv = gRDFContainerUtils->IndexToOrdinalResource(idx,
248 getter_AddRefs(ordinal));
249 if (NS_FAILED(rv)) return rv;
250
251 rv = mDataSource->Unassert(mContainer, ordinal, aElement);
252 if (NS_FAILED(rv)) return rv;
253
254 if (aRenumber) {
255 // Now slide the rest of the collection backwards to fill in
256 // the gap. This will have the side effect of completely
257 // renumber the container from index to the end.
258 rv = Renumber(idx + 1, -1);
259 if (NS_FAILED(rv)) return rv;
260 }
261
262 return NS_OK;
263 }
264
265
266 NS_IMETHODIMP
267 RDFContainerImpl::InsertElementAt(nsIRDFNode *aElement, int32_t aIndex, bool aRenumber)
268 {
269 if (!mDataSource || !mContainer)
270 return NS_ERROR_NOT_INITIALIZED;
271
272 NS_PRECONDITION(aElement != nullptr, "null ptr");
273 if (! aElement)
274 return NS_ERROR_NULL_POINTER;
275
276 NS_PRECONDITION(aIndex >= 1, "illegal value");
277 if (aIndex < 1)
278 return NS_ERROR_ILLEGAL_VALUE;
279
280 nsresult rv;
281
282 int32_t count;
283 rv = GetCount(&count);
284 if (NS_FAILED(rv)) return rv;
285
286 NS_ASSERTION(aIndex <= count + 1, "illegal value");
287 if (aIndex > count + 1)
288 return NS_ERROR_ILLEGAL_VALUE;
289
290 if (aRenumber) {
291 // Make a hole for the element. This will have the side effect of
292 // completely renumbering the container from 'aIndex' to 'count',
293 // and will spew assertions.
294 rv = Renumber(aIndex, +1);
295 if (NS_FAILED(rv)) return rv;
296 }
297
298 nsCOMPtr<nsIRDFResource> ordinal;
299 rv = gRDFContainerUtils->IndexToOrdinalResource(aIndex, getter_AddRefs(ordinal));
300 if (NS_FAILED(rv)) return rv;
301
302 rv = mDataSource->Assert(mContainer, ordinal, aElement, true);
303 if (NS_FAILED(rv)) return rv;
304
305 return NS_OK;
306 }
307
308 NS_IMETHODIMP
309 RDFContainerImpl::RemoveElementAt(int32_t aIndex, bool aRenumber, nsIRDFNode** _retval)
310 {
311 if (!mDataSource || !mContainer)
312 return NS_ERROR_NOT_INITIALIZED;
313
314 *_retval = nullptr;
315
316 if (aIndex< 1)
317 return NS_ERROR_ILLEGAL_VALUE;
318
319 nsresult rv;
320
321 int32_t count;
322 rv = GetCount(&count);
323 if (NS_FAILED(rv)) return rv;
324
325 if (aIndex > count)
326 return NS_ERROR_ILLEGAL_VALUE;
327
328 nsCOMPtr<nsIRDFResource> ordinal;
329 rv = gRDFContainerUtils->IndexToOrdinalResource(aIndex, getter_AddRefs(ordinal));
330 if (NS_FAILED(rv)) return rv;
331
332 nsCOMPtr<nsIRDFNode> old;
333 rv = mDataSource->GetTarget(mContainer, ordinal, true, getter_AddRefs(old));
334 if (NS_FAILED(rv)) return rv;
335
336 if (rv == NS_OK) {
337 rv = mDataSource->Unassert(mContainer, ordinal, old);
338 if (NS_FAILED(rv)) return rv;
339
340 if (aRenumber) {
341 // Now slide the rest of the collection backwards to fill in
342 // the gap. This will have the side effect of completely
343 // renumber the container from index to the end.
344 rv = Renumber(aIndex + 1, -1);
345 if (NS_FAILED(rv)) return rv;
346 }
347 }
348
349 old.swap(*_retval);
350
351 return NS_OK;
352 }
353
354 NS_IMETHODIMP
355 RDFContainerImpl::IndexOf(nsIRDFNode *aElement, int32_t *aIndex)
356 {
357 if (!mDataSource || !mContainer)
358 return NS_ERROR_NOT_INITIALIZED;
359
360 return gRDFContainerUtils->IndexOf(mDataSource, mContainer,
361 aElement, aIndex);
362 }
363
364
365 ////////////////////////////////////////////////////////////////////////
366
367
368 RDFContainerImpl::RDFContainerImpl()
369 : mDataSource(nullptr), mContainer(nullptr)
370 {
371 }
372
373
374 nsresult
375 RDFContainerImpl::Init()
376 {
377 if (gRefCnt++ == 0) {
378 nsresult rv;
379
380 NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID);
381 rv = CallGetService(kRDFServiceCID, &gRDFService);
382 if (NS_FAILED(rv)) {
383 NS_ERROR("unable to get RDF service");
384 return rv;
385 }
386
387 rv = gRDFService->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "nextVal"),
388 &kRDF_nextVal);
389 if (NS_FAILED(rv)) return rv;
390
391 NS_DEFINE_CID(kRDFContainerUtilsCID, NS_RDFCONTAINERUTILS_CID);
392 rv = CallGetService(kRDFContainerUtilsCID, &gRDFContainerUtils);
393 if (NS_FAILED(rv)) {
394 NS_ERROR("unable to get RDF container utils service");
395 return rv;
396 }
397 }
398
399 return NS_OK;
400 }
401
402
403 RDFContainerImpl::~RDFContainerImpl()
404 {
405 #ifdef DEBUG_REFS
406 --gInstanceCount;
407 fprintf(stdout, "%d - RDF: RDFContainerImpl\n", gInstanceCount);
408 #endif
409
410 NS_IF_RELEASE(mContainer);
411 NS_IF_RELEASE(mDataSource);
412
413 if (--gRefCnt == 0) {
414 NS_IF_RELEASE(gRDFContainerUtils);
415 NS_IF_RELEASE(gRDFService);
416 NS_IF_RELEASE(kRDF_nextVal);
417 }
418 }
419
420
421 nsresult
422 NS_NewRDFContainer(nsIRDFContainer** aResult)
423 {
424 RDFContainerImpl* result = new RDFContainerImpl();
425 if (! result)
426 return NS_ERROR_OUT_OF_MEMORY;
427
428 nsresult rv;
429 rv = result->Init();
430 if (NS_FAILED(rv)) {
431 delete result;
432 return rv;
433 }
434
435 NS_ADDREF(result);
436 *aResult = result;
437 return NS_OK;
438 }
439
440
441 nsresult
442 NS_NewRDFContainer(nsIRDFDataSource* aDataSource,
443 nsIRDFResource* aResource,
444 nsIRDFContainer** aResult)
445 {
446 nsresult rv;
447 rv = NS_NewRDFContainer(aResult);
448 if (NS_FAILED(rv)) return rv;
449
450 rv = (*aResult)->Init(aDataSource, aResource);
451 if (NS_FAILED(rv)) {
452 NS_RELEASE(*aResult);
453 }
454 return rv;
455 }
456
457
458 nsresult
459 RDFContainerImpl::Renumber(int32_t aStartIndex, int32_t aIncrement)
460 {
461 if (!mDataSource || !mContainer)
462 return NS_ERROR_NOT_INITIALIZED;
463
464 // Renumber the elements in the container starting with
465 // aStartIndex, updating each element's index by aIncrement. For
466 // example,
467 //
468 // (1:a 2:b 3:c)
469 // Renumber(2, +1);
470 // (1:a 3:b 4:c)
471 // Renumber(3, -1);
472 // (1:a 2:b 3:c)
473 //
474 nsresult rv;
475
476 if (! aIncrement)
477 return NS_OK;
478
479 int32_t count;
480 rv = GetCount(&count);
481 if (NS_FAILED(rv)) return rv;
482
483 if (aIncrement > 0) {
484 // Update the container's nextVal to reflect the
485 // renumbering. We do this now if aIncrement > 0 because we'll
486 // want to be able to acknowledge that new elements are in the
487 // container.
488 rv = SetNextValue(count + aIncrement + 1);
489 if (NS_FAILED(rv)) return rv;
490 }
491
492 int32_t i;
493 if (aIncrement < 0) {
494 i = aStartIndex;
495 }
496 else {
497 i = count; // we're one-indexed.
498 }
499
500 // Note: once we disable notifications, don't exit this method until
501 // enabling notifications
502 nsCOMPtr<nsIRDFPropagatableDataSource> propagatable =
503 do_QueryInterface(mDataSource);
504 if (propagatable) {
505 propagatable->SetPropagateChanges(false);
506 }
507
508 bool err = false;
509 while (!err && ((aIncrement < 0) ? (i <= count) : (i >= aStartIndex)))
510 {
511 nsCOMPtr<nsIRDFResource> oldOrdinal;
512 rv = gRDFContainerUtils->IndexToOrdinalResource(i, getter_AddRefs(oldOrdinal));
513 if (NS_FAILED(rv))
514 {
515 err = true;
516 continue;
517 }
518
519 nsCOMPtr<nsIRDFResource> newOrdinal;
520 rv = gRDFContainerUtils->IndexToOrdinalResource(i + aIncrement, getter_AddRefs(newOrdinal));
521 if (NS_FAILED(rv))
522 {
523 err = true;
524 continue;
525 }
526
527 // Because of aggregation, we need to be paranoid about the
528 // possibility that >1 element may be present per ordinal. If
529 // there _is_ in fact more than one element, they'll all get
530 // assigned to the same new ordinal; i.e., we don't make any
531 // attempt to "clean up" the duplicate numbering. (Doing so
532 // would require two passes.)
533 nsCOMPtr<nsISimpleEnumerator> targets;
534 rv = mDataSource->GetTargets(mContainer, oldOrdinal, true, getter_AddRefs(targets));
535 if (NS_FAILED(rv))
536 {
537 err = true;
538 continue;
539 }
540
541 while (1) {
542 bool hasMore;
543 rv = targets->HasMoreElements(&hasMore);
544 if (NS_FAILED(rv))
545 {
546 err = true;
547 break;
548 }
549
550 if (! hasMore)
551 break;
552
553 nsCOMPtr<nsISupports> isupports;
554 rv = targets->GetNext(getter_AddRefs(isupports));
555 if (NS_FAILED(rv))
556 {
557 err = true;
558 break;
559 }
560
561 nsCOMPtr<nsIRDFNode> element( do_QueryInterface(isupports) );
562 NS_ASSERTION(element != nullptr, "something funky in the enumerator");
563 if (! element)
564 {
565 err = true;
566 rv = NS_ERROR_UNEXPECTED;
567 break;
568 }
569
570 rv = mDataSource->Unassert(mContainer, oldOrdinal, element);
571 if (NS_FAILED(rv))
572 {
573 err = true;
574 break;
575 }
576
577 rv = mDataSource->Assert(mContainer, newOrdinal, element, true);
578 if (NS_FAILED(rv))
579 {
580 err = true;
581 break;
582 }
583 }
584
585 i -= aIncrement;
586 }
587
588 if (!err && (aIncrement < 0))
589 {
590 // Update the container's nextVal to reflect the
591 // renumbering. We do this now if aIncrement < 0 because, up
592 // until this point, we'll want people to be able to find
593 // things that are still "at the end".
594 rv = SetNextValue(count + aIncrement + 1);
595 if (NS_FAILED(rv))
596 {
597 err = true;
598 }
599 }
600
601 // Note: MUST enable notifications before exiting this method
602 if (propagatable) {
603 propagatable->SetPropagateChanges(true);
604 }
605
606 if (err) return(rv);
607
608 return NS_OK;
609 }
610
611
612
613 nsresult
614 RDFContainerImpl::SetNextValue(int32_t aIndex)
615 {
616 if (!mDataSource || !mContainer)
617 return NS_ERROR_NOT_INITIALIZED;
618
619 nsresult rv;
620
621 // Remove the current value of nextVal, if there is one.
622 nsCOMPtr<nsIRDFNode> nextValNode;
623 if (NS_SUCCEEDED(rv = mDataSource->GetTarget(mContainer,
624 kRDF_nextVal,
625 true,
626 getter_AddRefs(nextValNode)))) {
627 if (NS_FAILED(rv = mDataSource->Unassert(mContainer, kRDF_nextVal, nextValNode))) {
628 NS_ERROR("unable to update nextVal");
629 return rv;
630 }
631 }
632
633 nsAutoString s;
634 s.AppendInt(aIndex, 10);
635
636 nsCOMPtr<nsIRDFLiteral> nextVal;
637 if (NS_FAILED(rv = gRDFService->GetLiteral(s.get(), getter_AddRefs(nextVal)))) {
638 NS_ERROR("unable to get nextVal literal");
639 return rv;
640 }
641
642 rv = mDataSource->Assert(mContainer, kRDF_nextVal, nextVal, true);
643 if (rv != NS_RDF_ASSERTION_ACCEPTED) {
644 NS_ERROR("unable to update nextVal");
645 return NS_ERROR_FAILURE;
646 }
647
648 return NS_OK;
649 }
650
651
652 nsresult
653 RDFContainerImpl::GetNextValue(nsIRDFResource** aResult)
654 {
655 if (!mDataSource || !mContainer)
656 return NS_ERROR_NOT_INITIALIZED;
657
658 nsresult rv;
659
660 // Get the next value, which hangs off of the bag via the
661 // RDF:nextVal property.
662 nsCOMPtr<nsIRDFNode> nextValNode;
663 rv = mDataSource->GetTarget(mContainer, kRDF_nextVal, true, getter_AddRefs(nextValNode));
664 if (NS_FAILED(rv)) return rv;
665
666 if (rv == NS_RDF_NO_VALUE)
667 return NS_ERROR_UNEXPECTED;
668
669 nsCOMPtr<nsIRDFLiteral> nextValLiteral;
670 rv = nextValNode->QueryInterface(NS_GET_IID(nsIRDFLiteral), getter_AddRefs(nextValLiteral));
671 if (NS_FAILED(rv)) return rv;
672
673 const char16_t* s;
674 rv = nextValLiteral->GetValueConst(&s);
675 if (NS_FAILED(rv)) return rv;
676
677 int32_t nextVal = 0;
678 {
679 for (const char16_t* p = s; *p != 0; ++p) {
680 NS_ASSERTION(*p >= '0' && *p <= '9', "not a digit");
681 if (*p < '0' || *p > '9')
682 break;
683
684 nextVal *= 10;
685 nextVal += *p - '0';
686 }
687 }
688
689 static const char kRDFNameSpaceURI[] = RDF_NAMESPACE_URI;
690 char buf[sizeof(kRDFNameSpaceURI) + 16];
691 nsFixedCString nextValStr(buf, sizeof(buf), 0);
692 nextValStr = kRDFNameSpaceURI;
693 nextValStr.Append("_");
694 nextValStr.AppendInt(nextVal, 10);
695
696 rv = gRDFService->GetResource(nextValStr, aResult);
697 if (NS_FAILED(rv)) return rv;
698
699 // Now increment the RDF:nextVal property.
700 rv = mDataSource->Unassert(mContainer, kRDF_nextVal, nextValLiteral);
701 if (NS_FAILED(rv)) return rv;
702
703 ++nextVal;
704 nextValStr.Truncate();
705 nextValStr.AppendInt(nextVal, 10);
706
707 rv = gRDFService->GetLiteral(NS_ConvertASCIItoUTF16(nextValStr).get(), getter_AddRefs(nextValLiteral));
708 if (NS_FAILED(rv)) return rv;
709
710 rv = mDataSource->Assert(mContainer, kRDF_nextVal, nextValLiteral, true);
711 if (NS_FAILED(rv)) return rv;
712
713 if (RDF_SEQ_LIST_LIMIT == nextVal)
714 {
715 // focal point for RDF container mutation;
716 // basically, provide a hint to allow for fast access
717 nsCOMPtr<nsIRDFInMemoryDataSource> inMem = do_QueryInterface(mDataSource);
718 if (inMem)
719 {
720 // ignore error; failure just means slower access
721 (void)inMem->EnsureFastContainment(mContainer);
722 }
723 }
724
725 return NS_OK;
726 }

mercurial