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/. */
8 Implementation for the RDF container.
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.
37 #include "nsIRDFContainer.h"
38 #include "nsIRDFContainerUtils.h"
39 #include "nsIRDFInMemoryDataSource.h"
40 #include "nsIRDFPropagatableDataSource.h"
41 #include "nsIRDFService.h"
42 #include "nsIServiceManager.h"
45 #include "nsXPIDLString.h"
48 #define RDF_SEQ_LIST_LIMIT 8
50 class RDFContainerImpl
: public nsIRDFContainer
54 // nsISupports interface
57 // nsIRDFContainer interface
58 NS_DECL_NSIRDFCONTAINER
61 friend nsresult
NS_NewRDFContainer(nsIRDFContainer
** aResult
);
64 virtual ~RDFContainerImpl();
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
;
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
99 RDFContainerImpl::GetDataSource(nsIRDFDataSource
** _retval
)
101 *_retval
= mDataSource
;
102 NS_IF_ADDREF(*_retval
);
108 RDFContainerImpl::GetResource(nsIRDFResource
** _retval
)
110 *_retval
= mContainer
;
111 NS_IF_ADDREF(*_retval
);
117 RDFContainerImpl::Init(nsIRDFDataSource
*aDataSource
, nsIRDFResource
*aContainer
)
119 NS_PRECONDITION(aDataSource
!= nullptr, "null ptr");
121 return NS_ERROR_NULL_POINTER
;
123 NS_PRECONDITION(aContainer
!= nullptr, "null ptr");
125 return NS_ERROR_NULL_POINTER
;
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.
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
);
150 RDFContainerImpl::GetCount(int32_t *aCount
)
152 if (!mDataSource
|| !mContainer
)
153 return NS_ERROR_NOT_INITIALIZED
;
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
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
;
177 rv
= nextValLiteral
->GetValueConst( &s
);
178 if (NS_FAILED(rv
)) return rv
;
180 nsAutoString
nextValStr(s
);
184 nextVal
= nextValStr
.ToInteger(&err
);
186 return NS_ERROR_UNEXPECTED
;
188 *aCount
= nextVal
- 1;
194 RDFContainerImpl::GetElements(nsISimpleEnumerator
**_retval
)
196 if (!mDataSource
|| !mContainer
)
197 return NS_ERROR_NOT_INITIALIZED
;
199 return NS_NewContainerEnumerator(mDataSource
, mContainer
, _retval
);
204 RDFContainerImpl::AppendElement(nsIRDFNode
*aElement
)
206 if (!mDataSource
|| !mContainer
)
207 return NS_ERROR_NOT_INITIALIZED
;
209 NS_PRECONDITION(aElement
!= nullptr, "null ptr");
211 return NS_ERROR_NULL_POINTER
;
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
;
227 RDFContainerImpl::RemoveElement(nsIRDFNode
*aElement
, bool aRenumber
)
229 if (!mDataSource
|| !mContainer
)
230 return NS_ERROR_NOT_INITIALIZED
;
232 NS_PRECONDITION(aElement
!= nullptr, "null ptr");
234 return NS_ERROR_NULL_POINTER
;
239 rv
= IndexOf(aElement
, &idx
);
240 if (NS_FAILED(rv
)) return rv
;
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
;
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
;
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");
274 return NS_ERROR_NULL_POINTER
;
276 NS_PRECONDITION(aIndex
>= 1, "illegal value");
278 return NS_ERROR_ILLEGAL_VALUE
;
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
;
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
;
309 RDFContainerImpl::RemoveElementAt(int32_t aIndex
, bool aRenumber
, nsIRDFNode
** _retval
)
311 if (!mDataSource
|| !mContainer
)
312 return NS_ERROR_NOT_INITIALIZED
;
317 return NS_ERROR_ILLEGAL_VALUE
;
322 rv
= GetCount(&count
);
323 if (NS_FAILED(rv
)) return rv
;
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
;
337 rv
= mDataSource
->Unassert(mContainer
, ordinal
, old
);
338 if (NS_FAILED(rv
)) return rv
;
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
;
355 RDFContainerImpl::IndexOf(nsIRDFNode
*aElement
, int32_t *aIndex
)
357 if (!mDataSource
|| !mContainer
)
358 return NS_ERROR_NOT_INITIALIZED
;
360 return gRDFContainerUtils
->IndexOf(mDataSource
, mContainer
,
365 ////////////////////////////////////////////////////////////////////////
368 RDFContainerImpl::RDFContainerImpl()
369 : mDataSource(nullptr), mContainer(nullptr)
375 RDFContainerImpl::Init()
377 if (gRefCnt
++ == 0) {
380 NS_DEFINE_CID(kRDFServiceCID
, NS_RDFSERVICE_CID
);
381 rv
= CallGetService(kRDFServiceCID
, &gRDFService
);
383 NS_ERROR("unable to get RDF service");
387 rv
= gRDFService
->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI
"nextVal"),
389 if (NS_FAILED(rv
)) return rv
;
391 NS_DEFINE_CID(kRDFContainerUtilsCID
, NS_RDFCONTAINERUTILS_CID
);
392 rv
= CallGetService(kRDFContainerUtilsCID
, &gRDFContainerUtils
);
394 NS_ERROR("unable to get RDF container utils service");
403 RDFContainerImpl::~RDFContainerImpl()
407 fprintf(stdout
, "%d - RDF: RDFContainerImpl\n", gInstanceCount
);
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
);
422 NS_NewRDFContainer(nsIRDFContainer
** aResult
)
424 RDFContainerImpl
* result
= new RDFContainerImpl();
426 return NS_ERROR_OUT_OF_MEMORY
;
442 NS_NewRDFContainer(nsIRDFDataSource
* aDataSource
,
443 nsIRDFResource
* aResource
,
444 nsIRDFContainer
** aResult
)
447 rv
= NS_NewRDFContainer(aResult
);
448 if (NS_FAILED(rv
)) return rv
;
450 rv
= (*aResult
)->Init(aDataSource
, aResource
);
452 NS_RELEASE(*aResult
);
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
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
488 rv
= SetNextValue(count
+ aIncrement
+ 1);
489 if (NS_FAILED(rv
)) return rv
;
493 if (aIncrement
< 0) {
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
);
505 propagatable
->SetPropagateChanges(false);
509 while (!err
&& ((aIncrement
< 0) ? (i
<= count
) : (i
>= aStartIndex
)))
511 nsCOMPtr
<nsIRDFResource
> oldOrdinal
;
512 rv
= gRDFContainerUtils
->IndexToOrdinalResource(i
, getter_AddRefs(oldOrdinal
));
519 nsCOMPtr
<nsIRDFResource
> newOrdinal
;
520 rv
= gRDFContainerUtils
->IndexToOrdinalResource(i
+ aIncrement
, getter_AddRefs(newOrdinal
));
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
));
543 rv
= targets
->HasMoreElements(&hasMore
);
553 nsCOMPtr
<nsISupports
> isupports
;
554 rv
= targets
->GetNext(getter_AddRefs(isupports
));
561 nsCOMPtr
<nsIRDFNode
> element( do_QueryInterface(isupports
) );
562 NS_ASSERTION(element
!= nullptr, "something funky in the enumerator");
566 rv
= NS_ERROR_UNEXPECTED
;
570 rv
= mDataSource
->Unassert(mContainer
, oldOrdinal
, element
);
577 rv
= mDataSource
->Assert(mContainer
, newOrdinal
, element
, true);
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);
601 // Note: MUST enable notifications before exiting this method
603 propagatable
->SetPropagateChanges(true);
614 RDFContainerImpl::SetNextValue(int32_t aIndex
)
616 if (!mDataSource
|| !mContainer
)
617 return NS_ERROR_NOT_INITIALIZED
;
621 // Remove the current value of nextVal, if there is one.
622 nsCOMPtr
<nsIRDFNode
> nextValNode
;
623 if (NS_SUCCEEDED(rv
= mDataSource
->GetTarget(mContainer
,
626 getter_AddRefs(nextValNode
)))) {
627 if (NS_FAILED(rv
= mDataSource
->Unassert(mContainer
, kRDF_nextVal
, nextValNode
))) {
628 NS_ERROR("unable to update nextVal");
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");
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
;
653 RDFContainerImpl::GetNextValue(nsIRDFResource
** aResult
)
655 if (!mDataSource
|| !mContainer
)
656 return NS_ERROR_NOT_INITIALIZED
;
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
;
674 rv
= nextValLiteral
->GetValueConst(&s
);
675 if (NS_FAILED(rv
)) return rv
;
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')
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
;
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
);
720 // ignore error; failure just means slower access
721 (void)inMem
->EnsureFastContainment(mContainer
);