Bug 783551 - Get tooltool running on the b2g on OS X builds. r=respindola
[gecko.git] / rdf / base / src / nsRDFContainer.cpp
blob5f996ebfa669b82dd2ed974752922f1e4b77b771
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 static NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID);
49 static NS_DEFINE_CID(kRDFContainerUtilsCID, NS_RDFCONTAINERUTILS_CID);
50 static const char kRDFNameSpaceURI[] = RDF_NAMESPACE_URI;
52 #define RDF_SEQ_LIST_LIMIT 8
54 class RDFContainerImpl : public nsIRDFContainer
56 public:
58 // nsISupports interface
59 NS_DECL_ISUPPORTS
61 // nsIRDFContainer interface
62 NS_DECL_NSIRDFCONTAINER
64 private:
65 friend nsresult NS_NewRDFContainer(nsIRDFContainer** aResult);
67 RDFContainerImpl();
68 virtual ~RDFContainerImpl();
70 nsresult Init();
72 nsresult Renumber(PRInt32 aStartIndex, PRInt32 aIncrement);
73 nsresult SetNextValue(PRInt32 aIndex);
74 nsresult GetNextValue(nsIRDFResource** aResult);
76 nsIRDFDataSource* mDataSource;
77 nsIRDFResource* mContainer;
79 // pseudo constants
80 static PRInt32 gRefCnt;
81 static nsIRDFService* gRDFService;
82 static nsIRDFContainerUtils* gRDFContainerUtils;
83 static nsIRDFResource* kRDF_nextVal;
87 PRInt32 RDFContainerImpl::gRefCnt = 0;
88 nsIRDFService* RDFContainerImpl::gRDFService;
89 nsIRDFContainerUtils* RDFContainerImpl::gRDFContainerUtils;
90 nsIRDFResource* RDFContainerImpl::kRDF_nextVal;
92 ////////////////////////////////////////////////////////////////////////
93 // nsISupports interface
95 NS_IMPL_ISUPPORTS1(RDFContainerImpl, nsIRDFContainer)
99 ////////////////////////////////////////////////////////////////////////
100 // nsIRDFContainer interface
102 NS_IMETHODIMP
103 RDFContainerImpl::GetDataSource(nsIRDFDataSource** _retval)
105 *_retval = mDataSource;
106 NS_IF_ADDREF(*_retval);
107 return NS_OK;
111 NS_IMETHODIMP
112 RDFContainerImpl::GetResource(nsIRDFResource** _retval)
114 *_retval = mContainer;
115 NS_IF_ADDREF(*_retval);
116 return NS_OK;
120 NS_IMETHODIMP
121 RDFContainerImpl::Init(nsIRDFDataSource *aDataSource, nsIRDFResource *aContainer)
123 NS_PRECONDITION(aDataSource != nullptr, "null ptr");
124 if (! aDataSource)
125 return NS_ERROR_NULL_POINTER;
127 NS_PRECONDITION(aContainer != nullptr, "null ptr");
128 if (! aContainer)
129 return NS_ERROR_NULL_POINTER;
131 nsresult rv;
132 bool isContainer;
133 rv = gRDFContainerUtils->IsContainer(aDataSource, aContainer, &isContainer);
134 if (NS_FAILED(rv)) return rv;
136 // ``throw'' if we can't create a container on the specified
137 // datasource/resource combination.
138 if (! isContainer)
139 return NS_ERROR_FAILURE;
141 NS_IF_RELEASE(mDataSource);
142 mDataSource = aDataSource;
143 NS_ADDREF(mDataSource);
145 NS_IF_RELEASE(mContainer);
146 mContainer = aContainer;
147 NS_ADDREF(mContainer);
149 return NS_OK;
153 NS_IMETHODIMP
154 RDFContainerImpl::GetCount(PRInt32 *aCount)
156 if (!mDataSource || !mContainer)
157 return NS_ERROR_NOT_INITIALIZED;
159 nsresult rv;
161 // Get the next value, which hangs off of the bag via the
162 // RDF:nextVal property. This is the _next value_ that will get
163 // assigned in a one-indexed array. So, it's actually _one more_
164 // than the actual count of elements in the container.
166 // XXX To handle aggregation, this should probably be a
167 // GetTargets() that enumerates all of the values and picks the
168 // largest one.
169 nsCOMPtr<nsIRDFNode> nextValNode;
170 rv = mDataSource->GetTarget(mContainer, kRDF_nextVal, true, getter_AddRefs(nextValNode));
171 if (NS_FAILED(rv)) return rv;
173 if (rv == NS_RDF_NO_VALUE)
174 return NS_ERROR_UNEXPECTED;
176 nsCOMPtr<nsIRDFLiteral> nextValLiteral;
177 rv = nextValNode->QueryInterface(NS_GET_IID(nsIRDFLiteral), getter_AddRefs(nextValLiteral));
178 if (NS_FAILED(rv)) return rv;
180 const PRUnichar *s;
181 rv = nextValLiteral->GetValueConst( &s );
182 if (NS_FAILED(rv)) return rv;
184 nsAutoString nextValStr(s);
186 PRInt32 nextVal;
187 nsresult err;
188 nextVal = nextValStr.ToInteger(&err);
189 if (NS_FAILED(err))
190 return NS_ERROR_UNEXPECTED;
192 *aCount = nextVal - 1;
193 return NS_OK;
197 NS_IMETHODIMP
198 RDFContainerImpl::GetElements(nsISimpleEnumerator **_retval)
200 if (!mDataSource || !mContainer)
201 return NS_ERROR_NOT_INITIALIZED;
203 return NS_NewContainerEnumerator(mDataSource, mContainer, _retval);
207 NS_IMETHODIMP
208 RDFContainerImpl::AppendElement(nsIRDFNode *aElement)
210 if (!mDataSource || !mContainer)
211 return NS_ERROR_NOT_INITIALIZED;
213 NS_PRECONDITION(aElement != nullptr, "null ptr");
214 if (! aElement)
215 return NS_ERROR_NULL_POINTER;
217 nsresult rv;
219 nsCOMPtr<nsIRDFResource> nextVal;
220 rv = GetNextValue(getter_AddRefs(nextVal));
221 if (NS_FAILED(rv)) return rv;
223 rv = mDataSource->Assert(mContainer, nextVal, aElement, true);
224 if (NS_FAILED(rv)) return rv;
226 return NS_OK;
230 NS_IMETHODIMP
231 RDFContainerImpl::RemoveElement(nsIRDFNode *aElement, bool aRenumber)
233 if (!mDataSource || !mContainer)
234 return NS_ERROR_NOT_INITIALIZED;
236 NS_PRECONDITION(aElement != nullptr, "null ptr");
237 if (! aElement)
238 return NS_ERROR_NULL_POINTER;
240 nsresult rv;
242 PRInt32 idx;
243 rv = IndexOf(aElement, &idx);
244 if (NS_FAILED(rv)) return rv;
246 if (idx < 0)
247 return NS_OK;
249 // Remove the element.
250 nsCOMPtr<nsIRDFResource> ordinal;
251 rv = gRDFContainerUtils->IndexToOrdinalResource(idx,
252 getter_AddRefs(ordinal));
253 if (NS_FAILED(rv)) return rv;
255 rv = mDataSource->Unassert(mContainer, ordinal, aElement);
256 if (NS_FAILED(rv)) return rv;
258 if (aRenumber) {
259 // Now slide the rest of the collection backwards to fill in
260 // the gap. This will have the side effect of completely
261 // renumber the container from index to the end.
262 rv = Renumber(idx + 1, -1);
263 if (NS_FAILED(rv)) return rv;
266 return NS_OK;
270 NS_IMETHODIMP
271 RDFContainerImpl::InsertElementAt(nsIRDFNode *aElement, PRInt32 aIndex, bool aRenumber)
273 if (!mDataSource || !mContainer)
274 return NS_ERROR_NOT_INITIALIZED;
276 NS_PRECONDITION(aElement != nullptr, "null ptr");
277 if (! aElement)
278 return NS_ERROR_NULL_POINTER;
280 NS_PRECONDITION(aIndex >= 1, "illegal value");
281 if (aIndex < 1)
282 return NS_ERROR_ILLEGAL_VALUE;
284 nsresult rv;
286 PRInt32 count;
287 rv = GetCount(&count);
288 if (NS_FAILED(rv)) return rv;
290 NS_ASSERTION(aIndex <= count + 1, "illegal value");
291 if (aIndex > count + 1)
292 return NS_ERROR_ILLEGAL_VALUE;
294 if (aRenumber) {
295 // Make a hole for the element. This will have the side effect of
296 // completely renumbering the container from 'aIndex' to 'count',
297 // and will spew assertions.
298 rv = Renumber(aIndex, +1);
299 if (NS_FAILED(rv)) return rv;
302 nsCOMPtr<nsIRDFResource> ordinal;
303 rv = gRDFContainerUtils->IndexToOrdinalResource(aIndex, getter_AddRefs(ordinal));
304 if (NS_FAILED(rv)) return rv;
306 rv = mDataSource->Assert(mContainer, ordinal, aElement, true);
307 if (NS_FAILED(rv)) return rv;
309 return NS_OK;
312 NS_IMETHODIMP
313 RDFContainerImpl::RemoveElementAt(PRInt32 aIndex, bool aRenumber, nsIRDFNode** _retval)
315 if (!mDataSource || !mContainer)
316 return NS_ERROR_NOT_INITIALIZED;
318 *_retval = nullptr;
320 if (aIndex< 1)
321 return NS_ERROR_ILLEGAL_VALUE;
323 nsresult rv;
325 PRInt32 count;
326 rv = GetCount(&count);
327 if (NS_FAILED(rv)) return rv;
329 if (aIndex > count)
330 return NS_ERROR_ILLEGAL_VALUE;
332 nsCOMPtr<nsIRDFResource> ordinal;
333 rv = gRDFContainerUtils->IndexToOrdinalResource(aIndex, getter_AddRefs(ordinal));
334 if (NS_FAILED(rv)) return rv;
336 nsCOMPtr<nsIRDFNode> old;
337 rv = mDataSource->GetTarget(mContainer, ordinal, true, getter_AddRefs(old));
338 if (NS_FAILED(rv)) return rv;
340 if (rv == NS_OK) {
341 rv = mDataSource->Unassert(mContainer, ordinal, old);
342 if (NS_FAILED(rv)) return rv;
344 if (aRenumber) {
345 // Now slide the rest of the collection backwards to fill in
346 // the gap. This will have the side effect of completely
347 // renumber the container from index to the end.
348 rv = Renumber(aIndex + 1, -1);
349 if (NS_FAILED(rv)) return rv;
353 old.swap(*_retval);
355 return NS_OK;
358 NS_IMETHODIMP
359 RDFContainerImpl::IndexOf(nsIRDFNode *aElement, PRInt32 *aIndex)
361 if (!mDataSource || !mContainer)
362 return NS_ERROR_NOT_INITIALIZED;
364 return gRDFContainerUtils->IndexOf(mDataSource, mContainer,
365 aElement, aIndex);
369 ////////////////////////////////////////////////////////////////////////
372 RDFContainerImpl::RDFContainerImpl()
373 : mDataSource(nullptr), mContainer(nullptr)
378 nsresult
379 RDFContainerImpl::Init()
381 if (gRefCnt++ == 0) {
382 nsresult rv;
384 rv = CallGetService(kRDFServiceCID, &gRDFService);
385 if (NS_FAILED(rv)) {
386 NS_ERROR("unable to get RDF service");
387 return rv;
390 rv = gRDFService->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "nextVal"),
391 &kRDF_nextVal);
392 if (NS_FAILED(rv)) return rv;
394 rv = CallGetService(kRDFContainerUtilsCID, &gRDFContainerUtils);
395 if (NS_FAILED(rv)) {
396 NS_ERROR("unable to get RDF container utils service");
397 return rv;
401 return NS_OK;
405 RDFContainerImpl::~RDFContainerImpl()
407 #ifdef DEBUG_REFS
408 --gInstanceCount;
409 fprintf(stdout, "%d - RDF: RDFContainerImpl\n", gInstanceCount);
410 #endif
412 NS_IF_RELEASE(mContainer);
413 NS_IF_RELEASE(mDataSource);
415 if (--gRefCnt == 0) {
416 NS_IF_RELEASE(gRDFContainerUtils);
417 NS_IF_RELEASE(gRDFService);
418 NS_IF_RELEASE(kRDF_nextVal);
423 nsresult
424 NS_NewRDFContainer(nsIRDFContainer** aResult)
426 RDFContainerImpl* result = new RDFContainerImpl();
427 if (! result)
428 return NS_ERROR_OUT_OF_MEMORY;
430 nsresult rv;
431 rv = result->Init();
432 if (NS_FAILED(rv)) {
433 delete result;
434 return rv;
437 NS_ADDREF(result);
438 *aResult = result;
439 return NS_OK;
443 nsresult
444 NS_NewRDFContainer(nsIRDFDataSource* aDataSource,
445 nsIRDFResource* aResource,
446 nsIRDFContainer** aResult)
448 nsresult rv;
449 rv = NS_NewRDFContainer(aResult);
450 if (NS_FAILED(rv)) return rv;
452 rv = (*aResult)->Init(aDataSource, aResource);
453 if (NS_FAILED(rv)) {
454 NS_RELEASE(*aResult);
456 return rv;
460 nsresult
461 RDFContainerImpl::Renumber(PRInt32 aStartIndex, PRInt32 aIncrement)
463 if (!mDataSource || !mContainer)
464 return NS_ERROR_NOT_INITIALIZED;
466 // Renumber the elements in the container starting with
467 // aStartIndex, updating each element's index by aIncrement. For
468 // example,
470 // (1:a 2:b 3:c)
471 // Renumber(2, +1);
472 // (1:a 3:b 4:c)
473 // Renumber(3, -1);
474 // (1:a 2:b 3:c)
476 nsresult rv;
478 if (! aIncrement)
479 return NS_OK;
481 PRInt32 count;
482 rv = GetCount(&count);
483 if (NS_FAILED(rv)) return rv;
485 if (aIncrement > 0) {
486 // Update the container's nextVal to reflect the
487 // renumbering. We do this now if aIncrement > 0 because we'll
488 // want to be able to acknowledge that new elements are in the
489 // container.
490 rv = SetNextValue(count + aIncrement + 1);
491 if (NS_FAILED(rv)) return rv;
494 PRInt32 i;
495 if (aIncrement < 0) {
496 i = aStartIndex;
498 else {
499 i = count; // we're one-indexed.
502 // Note: once we disable notifications, don't exit this method until
503 // enabling notifications
504 nsCOMPtr<nsIRDFPropagatableDataSource> propagatable =
505 do_QueryInterface(mDataSource);
506 if (propagatable) {
507 propagatable->SetPropagateChanges(false);
510 bool err = false;
511 while (!err && ((aIncrement < 0) ? (i <= count) : (i >= aStartIndex)))
513 nsCOMPtr<nsIRDFResource> oldOrdinal;
514 rv = gRDFContainerUtils->IndexToOrdinalResource(i, getter_AddRefs(oldOrdinal));
515 if (NS_FAILED(rv))
517 err = true;
518 continue;
521 nsCOMPtr<nsIRDFResource> newOrdinal;
522 rv = gRDFContainerUtils->IndexToOrdinalResource(i + aIncrement, getter_AddRefs(newOrdinal));
523 if (NS_FAILED(rv))
525 err = true;
526 continue;
529 // Because of aggregation, we need to be paranoid about the
530 // possibility that >1 element may be present per ordinal. If
531 // there _is_ in fact more than one element, they'll all get
532 // assigned to the same new ordinal; i.e., we don't make any
533 // attempt to "clean up" the duplicate numbering. (Doing so
534 // would require two passes.)
535 nsCOMPtr<nsISimpleEnumerator> targets;
536 rv = mDataSource->GetTargets(mContainer, oldOrdinal, true, getter_AddRefs(targets));
537 if (NS_FAILED(rv))
539 err = true;
540 continue;
543 while (1) {
544 bool hasMore;
545 rv = targets->HasMoreElements(&hasMore);
546 if (NS_FAILED(rv))
548 err = true;
549 break;
552 if (! hasMore)
553 break;
555 nsCOMPtr<nsISupports> isupports;
556 rv = targets->GetNext(getter_AddRefs(isupports));
557 if (NS_FAILED(rv))
559 err = true;
560 break;
563 nsCOMPtr<nsIRDFNode> element( do_QueryInterface(isupports) );
564 NS_ASSERTION(element != nullptr, "something funky in the enumerator");
565 if (! element)
567 err = true;
568 rv = NS_ERROR_UNEXPECTED;
569 break;
572 rv = mDataSource->Unassert(mContainer, oldOrdinal, element);
573 if (NS_FAILED(rv))
575 err = true;
576 break;
579 rv = mDataSource->Assert(mContainer, newOrdinal, element, true);
580 if (NS_FAILED(rv))
582 err = true;
583 break;
587 i -= aIncrement;
590 if (!err && (aIncrement < 0))
592 // Update the container's nextVal to reflect the
593 // renumbering. We do this now if aIncrement < 0 because, up
594 // until this point, we'll want people to be able to find
595 // things that are still "at the end".
596 rv = SetNextValue(count + aIncrement + 1);
597 if (NS_FAILED(rv))
599 err = true;
603 // Note: MUST enable notifications before exiting this method
604 if (propagatable) {
605 propagatable->SetPropagateChanges(true);
608 if (err) return(rv);
610 return NS_OK;
615 nsresult
616 RDFContainerImpl::SetNextValue(PRInt32 aIndex)
618 if (!mDataSource || !mContainer)
619 return NS_ERROR_NOT_INITIALIZED;
621 nsresult rv;
623 // Remove the current value of nextVal, if there is one.
624 nsCOMPtr<nsIRDFNode> nextValNode;
625 if (NS_SUCCEEDED(rv = mDataSource->GetTarget(mContainer,
626 kRDF_nextVal,
627 true,
628 getter_AddRefs(nextValNode)))) {
629 if (NS_FAILED(rv = mDataSource->Unassert(mContainer, kRDF_nextVal, nextValNode))) {
630 NS_ERROR("unable to update nextVal");
631 return rv;
635 nsAutoString s;
636 s.AppendInt(aIndex, 10);
638 nsCOMPtr<nsIRDFLiteral> nextVal;
639 if (NS_FAILED(rv = gRDFService->GetLiteral(s.get(), getter_AddRefs(nextVal)))) {
640 NS_ERROR("unable to get nextVal literal");
641 return rv;
644 rv = mDataSource->Assert(mContainer, kRDF_nextVal, nextVal, true);
645 if (rv != NS_RDF_ASSERTION_ACCEPTED) {
646 NS_ERROR("unable to update nextVal");
647 return NS_ERROR_FAILURE;
650 return NS_OK;
654 nsresult
655 RDFContainerImpl::GetNextValue(nsIRDFResource** aResult)
657 if (!mDataSource || !mContainer)
658 return NS_ERROR_NOT_INITIALIZED;
660 nsresult rv;
662 // Get the next value, which hangs off of the bag via the
663 // RDF:nextVal property.
664 nsCOMPtr<nsIRDFNode> nextValNode;
665 rv = mDataSource->GetTarget(mContainer, kRDF_nextVal, true, getter_AddRefs(nextValNode));
666 if (NS_FAILED(rv)) return rv;
668 if (rv == NS_RDF_NO_VALUE)
669 return NS_ERROR_UNEXPECTED;
671 nsCOMPtr<nsIRDFLiteral> nextValLiteral;
672 rv = nextValNode->QueryInterface(NS_GET_IID(nsIRDFLiteral), getter_AddRefs(nextValLiteral));
673 if (NS_FAILED(rv)) return rv;
675 const PRUnichar* s;
676 rv = nextValLiteral->GetValueConst(&s);
677 if (NS_FAILED(rv)) return rv;
679 PRInt32 nextVal = 0;
681 for (const PRUnichar* p = s; *p != 0; ++p) {
682 NS_ASSERTION(*p >= '0' && *p <= '9', "not a digit");
683 if (*p < '0' || *p > '9')
684 break;
686 nextVal *= 10;
687 nextVal += *p - '0';
691 char buf[sizeof(kRDFNameSpaceURI) + 16];
692 nsFixedCString nextValStr(buf, sizeof(buf), 0);
693 nextValStr = kRDFNameSpaceURI;
694 nextValStr.Append("_");
695 nextValStr.AppendInt(nextVal, 10);
697 rv = gRDFService->GetResource(nextValStr, aResult);
698 if (NS_FAILED(rv)) return rv;
700 // Now increment the RDF:nextVal property.
701 rv = mDataSource->Unassert(mContainer, kRDF_nextVal, nextValLiteral);
702 if (NS_FAILED(rv)) return rv;
704 ++nextVal;
705 nextValStr.Truncate();
706 nextValStr.AppendInt(nextVal, 10);
708 rv = gRDFService->GetLiteral(NS_ConvertASCIItoUTF16(nextValStr).get(), getter_AddRefs(nextValLiteral));
709 if (NS_FAILED(rv)) return rv;
711 rv = mDataSource->Assert(mContainer, kRDF_nextVal, nextValLiteral, true);
712 if (NS_FAILED(rv)) return rv;
714 if (RDF_SEQ_LIST_LIMIT == nextVal)
716 // focal point for RDF container mutation;
717 // basically, provide a hint to allow for fast access
718 nsCOMPtr<nsIRDFInMemoryDataSource> inMem = do_QueryInterface(mDataSource);
719 if (inMem)
721 // ignore error; failure just means slower access
722 (void)inMem->EnsureFastContainment(mContainer);
726 return NS_OK;