Bumping manifests a=b2g-bump
[gecko.git] / rdf / base / nsRDFContainer.cpp
blob6000c70d55404fed27e2811145fd177a413c5669
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/. */
6 /*
8 Implementation for the RDF container.
10 Notes
11 -----
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:
16 for (i = 0; i < count; ++i) {}
18 You've gotta write like this:
20 for (i = 1; i <= count; ++i) {}
22 "Sure, right, yeah, of course.", you say. Well maybe I'm just
23 thick, but it's easy to slip up.
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.
30 3. The RDF:nextVal property is also used to retrieve the count of
31 elements in the container.
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"
48 #define RDF_SEQ_LIST_LIMIT 8
50 class RDFContainerImpl : public nsIRDFContainer
52 public:
54 // nsISupports interface
55 NS_DECL_ISUPPORTS
57 // nsIRDFContainer interface
58 NS_DECL_NSIRDFCONTAINER
60 private:
61 friend nsresult NS_NewRDFContainer(nsIRDFContainer** aResult);
63 RDFContainerImpl();
64 virtual ~RDFContainerImpl();
66 nsresult Init();
68 nsresult Renumber(int32_t aStartIndex, int32_t aIncrement);
69 nsresult SetNextValue(int32_t aIndex);
70 nsresult GetNextValue(nsIRDFResource** aResult);
72 nsIRDFDataSource* mDataSource;
73 nsIRDFResource* mContainer;
75 // pseudo constants
76 static int32_t gRefCnt;
77 static nsIRDFService* gRDFService;
78 static nsIRDFContainerUtils* gRDFContainerUtils;
79 static nsIRDFResource* kRDF_nextVal;
83 int32_t RDFContainerImpl::gRefCnt = 0;
84 nsIRDFService* RDFContainerImpl::gRDFService;
85 nsIRDFContainerUtils* RDFContainerImpl::gRDFContainerUtils;
86 nsIRDFResource* RDFContainerImpl::kRDF_nextVal;
88 ////////////////////////////////////////////////////////////////////////
89 // nsISupports interface
91 NS_IMPL_ISUPPORTS(RDFContainerImpl, nsIRDFContainer)
95 ////////////////////////////////////////////////////////////////////////
96 // nsIRDFContainer interface
98 NS_IMETHODIMP
99 RDFContainerImpl::GetDataSource(nsIRDFDataSource** _retval)
101 *_retval = mDataSource;
102 NS_IF_ADDREF(*_retval);
103 return NS_OK;
107 NS_IMETHODIMP
108 RDFContainerImpl::GetResource(nsIRDFResource** _retval)
110 *_retval = mContainer;
111 NS_IF_ADDREF(*_retval);
112 return NS_OK;
116 NS_IMETHODIMP
117 RDFContainerImpl::Init(nsIRDFDataSource *aDataSource, nsIRDFResource *aContainer)
119 NS_PRECONDITION(aDataSource != nullptr, "null ptr");
120 if (! aDataSource)
121 return NS_ERROR_NULL_POINTER;
123 NS_PRECONDITION(aContainer != nullptr, "null ptr");
124 if (! aContainer)
125 return NS_ERROR_NULL_POINTER;
127 nsresult rv;
128 bool isContainer;
129 rv = gRDFContainerUtils->IsContainer(aDataSource, aContainer, &isContainer);
130 if (NS_FAILED(rv)) return rv;
132 // ``throw'' if we can't create a container on the specified
133 // datasource/resource combination.
134 if (! isContainer)
135 return NS_ERROR_FAILURE;
137 NS_IF_RELEASE(mDataSource);
138 mDataSource = aDataSource;
139 NS_ADDREF(mDataSource);
141 NS_IF_RELEASE(mContainer);
142 mContainer = aContainer;
143 NS_ADDREF(mContainer);
145 return NS_OK;
149 NS_IMETHODIMP
150 RDFContainerImpl::GetCount(int32_t *aCount)
152 if (!mDataSource || !mContainer)
153 return NS_ERROR_NOT_INITIALIZED;
155 nsresult rv;
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.
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;
169 if (rv == NS_RDF_NO_VALUE)
170 return NS_ERROR_UNEXPECTED;
172 nsCOMPtr<nsIRDFLiteral> nextValLiteral;
173 rv = nextValNode->QueryInterface(NS_GET_IID(nsIRDFLiteral), getter_AddRefs(nextValLiteral));
174 if (NS_FAILED(rv)) return rv;
176 const char16_t *s;
177 rv = nextValLiteral->GetValueConst( &s );
178 if (NS_FAILED(rv)) return rv;
180 nsAutoString nextValStr(s);
182 int32_t nextVal;
183 nsresult err;
184 nextVal = nextValStr.ToInteger(&err);
185 if (NS_FAILED(err))
186 return NS_ERROR_UNEXPECTED;
188 *aCount = nextVal - 1;
189 return NS_OK;
193 NS_IMETHODIMP
194 RDFContainerImpl::GetElements(nsISimpleEnumerator **_retval)
196 if (!mDataSource || !mContainer)
197 return NS_ERROR_NOT_INITIALIZED;
199 return NS_NewContainerEnumerator(mDataSource, mContainer, _retval);
203 NS_IMETHODIMP
204 RDFContainerImpl::AppendElement(nsIRDFNode *aElement)
206 if (!mDataSource || !mContainer)
207 return NS_ERROR_NOT_INITIALIZED;
209 NS_PRECONDITION(aElement != nullptr, "null ptr");
210 if (! aElement)
211 return NS_ERROR_NULL_POINTER;
213 nsresult rv;
215 nsCOMPtr<nsIRDFResource> nextVal;
216 rv = GetNextValue(getter_AddRefs(nextVal));
217 if (NS_FAILED(rv)) return rv;
219 rv = mDataSource->Assert(mContainer, nextVal, aElement, true);
220 if (NS_FAILED(rv)) return rv;
222 return NS_OK;
226 NS_IMETHODIMP
227 RDFContainerImpl::RemoveElement(nsIRDFNode *aElement, bool aRenumber)
229 if (!mDataSource || !mContainer)
230 return NS_ERROR_NOT_INITIALIZED;
232 NS_PRECONDITION(aElement != nullptr, "null ptr");
233 if (! aElement)
234 return NS_ERROR_NULL_POINTER;
236 nsresult rv;
238 int32_t idx;
239 rv = IndexOf(aElement, &idx);
240 if (NS_FAILED(rv)) return rv;
242 if (idx < 0)
243 return NS_OK;
245 // Remove the element.
246 nsCOMPtr<nsIRDFResource> ordinal;
247 rv = gRDFContainerUtils->IndexToOrdinalResource(idx,
248 getter_AddRefs(ordinal));
249 if (NS_FAILED(rv)) return rv;
251 rv = mDataSource->Unassert(mContainer, ordinal, aElement);
252 if (NS_FAILED(rv)) return rv;
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;
262 return NS_OK;
266 NS_IMETHODIMP
267 RDFContainerImpl::InsertElementAt(nsIRDFNode *aElement, int32_t aIndex, bool aRenumber)
269 if (!mDataSource || !mContainer)
270 return NS_ERROR_NOT_INITIALIZED;
272 NS_PRECONDITION(aElement != nullptr, "null ptr");
273 if (! aElement)
274 return NS_ERROR_NULL_POINTER;
276 NS_PRECONDITION(aIndex >= 1, "illegal value");
277 if (aIndex < 1)
278 return NS_ERROR_ILLEGAL_VALUE;
280 nsresult rv;
282 int32_t count;
283 rv = GetCount(&count);
284 if (NS_FAILED(rv)) return rv;
286 NS_ASSERTION(aIndex <= count + 1, "illegal value");
287 if (aIndex > count + 1)
288 return NS_ERROR_ILLEGAL_VALUE;
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;
298 nsCOMPtr<nsIRDFResource> ordinal;
299 rv = gRDFContainerUtils->IndexToOrdinalResource(aIndex, getter_AddRefs(ordinal));
300 if (NS_FAILED(rv)) return rv;
302 rv = mDataSource->Assert(mContainer, ordinal, aElement, true);
303 if (NS_FAILED(rv)) return rv;
305 return NS_OK;
308 NS_IMETHODIMP
309 RDFContainerImpl::RemoveElementAt(int32_t aIndex, bool aRenumber, nsIRDFNode** _retval)
311 if (!mDataSource || !mContainer)
312 return NS_ERROR_NOT_INITIALIZED;
314 *_retval = nullptr;
316 if (aIndex< 1)
317 return NS_ERROR_ILLEGAL_VALUE;
319 nsresult rv;
321 int32_t count;
322 rv = GetCount(&count);
323 if (NS_FAILED(rv)) return rv;
325 if (aIndex > count)
326 return NS_ERROR_ILLEGAL_VALUE;
328 nsCOMPtr<nsIRDFResource> ordinal;
329 rv = gRDFContainerUtils->IndexToOrdinalResource(aIndex, getter_AddRefs(ordinal));
330 if (NS_FAILED(rv)) return rv;
332 nsCOMPtr<nsIRDFNode> old;
333 rv = mDataSource->GetTarget(mContainer, ordinal, true, getter_AddRefs(old));
334 if (NS_FAILED(rv)) return rv;
336 if (rv == NS_OK) {
337 rv = mDataSource->Unassert(mContainer, ordinal, old);
338 if (NS_FAILED(rv)) return rv;
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;
349 old.swap(*_retval);
351 return NS_OK;
354 NS_IMETHODIMP
355 RDFContainerImpl::IndexOf(nsIRDFNode *aElement, int32_t *aIndex)
357 if (!mDataSource || !mContainer)
358 return NS_ERROR_NOT_INITIALIZED;
360 return gRDFContainerUtils->IndexOf(mDataSource, mContainer,
361 aElement, aIndex);
365 ////////////////////////////////////////////////////////////////////////
368 RDFContainerImpl::RDFContainerImpl()
369 : mDataSource(nullptr), mContainer(nullptr)
374 nsresult
375 RDFContainerImpl::Init()
377 if (gRefCnt++ == 0) {
378 nsresult rv;
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;
387 rv = gRDFService->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "nextVal"),
388 &kRDF_nextVal);
389 if (NS_FAILED(rv)) return rv;
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;
399 return NS_OK;
403 RDFContainerImpl::~RDFContainerImpl()
405 #ifdef DEBUG_REFS
406 --gInstanceCount;
407 fprintf(stdout, "%d - RDF: RDFContainerImpl\n", gInstanceCount);
408 #endif
410 NS_IF_RELEASE(mContainer);
411 NS_IF_RELEASE(mDataSource);
413 if (--gRefCnt == 0) {
414 NS_IF_RELEASE(gRDFContainerUtils);
415 NS_IF_RELEASE(gRDFService);
416 NS_IF_RELEASE(kRDF_nextVal);
421 nsresult
422 NS_NewRDFContainer(nsIRDFContainer** aResult)
424 RDFContainerImpl* result = new RDFContainerImpl();
425 if (! result)
426 return NS_ERROR_OUT_OF_MEMORY;
428 nsresult rv;
429 rv = result->Init();
430 if (NS_FAILED(rv)) {
431 delete result;
432 return rv;
435 NS_ADDREF(result);
436 *aResult = result;
437 return NS_OK;
441 nsresult
442 NS_NewRDFContainer(nsIRDFDataSource* aDataSource,
443 nsIRDFResource* aResource,
444 nsIRDFContainer** aResult)
446 nsresult rv;
447 rv = NS_NewRDFContainer(aResult);
448 if (NS_FAILED(rv)) return rv;
450 rv = (*aResult)->Init(aDataSource, aResource);
451 if (NS_FAILED(rv)) {
452 NS_RELEASE(*aResult);
454 return rv;
458 nsresult
459 RDFContainerImpl::Renumber(int32_t aStartIndex, int32_t aIncrement)
461 if (!mDataSource || !mContainer)
462 return NS_ERROR_NOT_INITIALIZED;
464 // Renumber the elements in the container starting with
465 // aStartIndex, updating each element's index by aIncrement. For
466 // example,
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)
474 nsresult rv;
476 if (! aIncrement)
477 return NS_OK;
479 int32_t count;
480 rv = GetCount(&count);
481 if (NS_FAILED(rv)) return rv;
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;
492 int32_t i;
493 if (aIncrement < 0) {
494 i = aStartIndex;
496 else {
497 i = count; // we're one-indexed.
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);
508 bool err = false;
509 while (!err && ((aIncrement < 0) ? (i <= count) : (i >= aStartIndex)))
511 nsCOMPtr<nsIRDFResource> oldOrdinal;
512 rv = gRDFContainerUtils->IndexToOrdinalResource(i, getter_AddRefs(oldOrdinal));
513 if (NS_FAILED(rv))
515 err = true;
516 continue;
519 nsCOMPtr<nsIRDFResource> newOrdinal;
520 rv = gRDFContainerUtils->IndexToOrdinalResource(i + aIncrement, getter_AddRefs(newOrdinal));
521 if (NS_FAILED(rv))
523 err = true;
524 continue;
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))
537 err = true;
538 continue;
541 while (1) {
542 bool hasMore;
543 rv = targets->HasMoreElements(&hasMore);
544 if (NS_FAILED(rv))
546 err = true;
547 break;
550 if (! hasMore)
551 break;
553 nsCOMPtr<nsISupports> isupports;
554 rv = targets->GetNext(getter_AddRefs(isupports));
555 if (NS_FAILED(rv))
557 err = true;
558 break;
561 nsCOMPtr<nsIRDFNode> element( do_QueryInterface(isupports) );
562 NS_ASSERTION(element != nullptr, "something funky in the enumerator");
563 if (! element)
565 err = true;
566 rv = NS_ERROR_UNEXPECTED;
567 break;
570 rv = mDataSource->Unassert(mContainer, oldOrdinal, element);
571 if (NS_FAILED(rv))
573 err = true;
574 break;
577 rv = mDataSource->Assert(mContainer, newOrdinal, element, true);
578 if (NS_FAILED(rv))
580 err = true;
581 break;
585 i -= aIncrement;
588 if (!err && (aIncrement < 0))
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))
597 err = true;
601 // Note: MUST enable notifications before exiting this method
602 if (propagatable) {
603 propagatable->SetPropagateChanges(true);
606 if (err) return(rv);
608 return NS_OK;
613 nsresult
614 RDFContainerImpl::SetNextValue(int32_t aIndex)
616 if (!mDataSource || !mContainer)
617 return NS_ERROR_NOT_INITIALIZED;
619 nsresult rv;
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;
633 nsAutoString s;
634 s.AppendInt(aIndex, 10);
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;
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;
648 return NS_OK;
652 nsresult
653 RDFContainerImpl::GetNextValue(nsIRDFResource** aResult)
655 if (!mDataSource || !mContainer)
656 return NS_ERROR_NOT_INITIALIZED;
658 nsresult rv;
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;
666 if (rv == NS_RDF_NO_VALUE)
667 return NS_ERROR_UNEXPECTED;
669 nsCOMPtr<nsIRDFLiteral> nextValLiteral;
670 rv = nextValNode->QueryInterface(NS_GET_IID(nsIRDFLiteral), getter_AddRefs(nextValLiteral));
671 if (NS_FAILED(rv)) return rv;
673 const char16_t* s;
674 rv = nextValLiteral->GetValueConst(&s);
675 if (NS_FAILED(rv)) return rv;
677 int32_t nextVal = 0;
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;
684 nextVal *= 10;
685 nextVal += *p - '0';
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);
696 rv = gRDFService->GetResource(nextValStr, aResult);
697 if (NS_FAILED(rv)) return rv;
699 // Now increment the RDF:nextVal property.
700 rv = mDataSource->Unassert(mContainer, kRDF_nextVal, nextValLiteral);
701 if (NS_FAILED(rv)) return rv;
703 ++nextVal;
704 nextValStr.Truncate();
705 nextValStr.AppendInt(nextVal, 10);
707 rv = gRDFService->GetLiteral(NS_ConvertASCIItoUTF16(nextValStr).get(), getter_AddRefs(nextValLiteral));
708 if (NS_FAILED(rv)) return rv;
710 rv = mDataSource->Assert(mContainer, kRDF_nextVal, nextValLiteral, true);
711 if (NS_FAILED(rv)) return rv;
713 if (RDF_SEQ_LIST_LIMIT == nextVal)
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)
720 // ignore error; failure just means slower access
721 (void)inMem->EnsureFastContainment(mContainer);
725 return NS_OK;