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 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
58 // nsISupports interface
61 // nsIRDFContainer interface
62 NS_DECL_NSIRDFCONTAINER
65 friend nsresult
NS_NewRDFContainer(nsIRDFContainer
** aResult
);
68 virtual ~RDFContainerImpl();
72 nsresult
Renumber(PRInt32 aStartIndex
, PRInt32 aIncrement
);
73 nsresult
SetNextValue(PRInt32 aIndex
);
74 nsresult
GetNextValue(nsIRDFResource
** aResult
);
76 nsIRDFDataSource
* mDataSource
;
77 nsIRDFResource
* mContainer
;
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
103 RDFContainerImpl::GetDataSource(nsIRDFDataSource
** _retval
)
105 *_retval
= mDataSource
;
106 NS_IF_ADDREF(*_retval
);
112 RDFContainerImpl::GetResource(nsIRDFResource
** _retval
)
114 *_retval
= mContainer
;
115 NS_IF_ADDREF(*_retval
);
121 RDFContainerImpl::Init(nsIRDFDataSource
*aDataSource
, nsIRDFResource
*aContainer
)
123 NS_PRECONDITION(aDataSource
!= nullptr, "null ptr");
125 return NS_ERROR_NULL_POINTER
;
127 NS_PRECONDITION(aContainer
!= nullptr, "null ptr");
129 return NS_ERROR_NULL_POINTER
;
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.
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
);
154 RDFContainerImpl::GetCount(PRInt32
*aCount
)
156 if (!mDataSource
|| !mContainer
)
157 return NS_ERROR_NOT_INITIALIZED
;
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
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
;
181 rv
= nextValLiteral
->GetValueConst( &s
);
182 if (NS_FAILED(rv
)) return rv
;
184 nsAutoString
nextValStr(s
);
188 nextVal
= nextValStr
.ToInteger(&err
);
190 return NS_ERROR_UNEXPECTED
;
192 *aCount
= nextVal
- 1;
198 RDFContainerImpl::GetElements(nsISimpleEnumerator
**_retval
)
200 if (!mDataSource
|| !mContainer
)
201 return NS_ERROR_NOT_INITIALIZED
;
203 return NS_NewContainerEnumerator(mDataSource
, mContainer
, _retval
);
208 RDFContainerImpl::AppendElement(nsIRDFNode
*aElement
)
210 if (!mDataSource
|| !mContainer
)
211 return NS_ERROR_NOT_INITIALIZED
;
213 NS_PRECONDITION(aElement
!= nullptr, "null ptr");
215 return NS_ERROR_NULL_POINTER
;
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
;
231 RDFContainerImpl::RemoveElement(nsIRDFNode
*aElement
, bool aRenumber
)
233 if (!mDataSource
|| !mContainer
)
234 return NS_ERROR_NOT_INITIALIZED
;
236 NS_PRECONDITION(aElement
!= nullptr, "null ptr");
238 return NS_ERROR_NULL_POINTER
;
243 rv
= IndexOf(aElement
, &idx
);
244 if (NS_FAILED(rv
)) return rv
;
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
;
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
;
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");
278 return NS_ERROR_NULL_POINTER
;
280 NS_PRECONDITION(aIndex
>= 1, "illegal value");
282 return NS_ERROR_ILLEGAL_VALUE
;
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
;
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
;
313 RDFContainerImpl::RemoveElementAt(PRInt32 aIndex
, bool aRenumber
, nsIRDFNode
** _retval
)
315 if (!mDataSource
|| !mContainer
)
316 return NS_ERROR_NOT_INITIALIZED
;
321 return NS_ERROR_ILLEGAL_VALUE
;
326 rv
= GetCount(&count
);
327 if (NS_FAILED(rv
)) return rv
;
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
;
341 rv
= mDataSource
->Unassert(mContainer
, ordinal
, old
);
342 if (NS_FAILED(rv
)) return rv
;
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
;
359 RDFContainerImpl::IndexOf(nsIRDFNode
*aElement
, PRInt32
*aIndex
)
361 if (!mDataSource
|| !mContainer
)
362 return NS_ERROR_NOT_INITIALIZED
;
364 return gRDFContainerUtils
->IndexOf(mDataSource
, mContainer
,
369 ////////////////////////////////////////////////////////////////////////
372 RDFContainerImpl::RDFContainerImpl()
373 : mDataSource(nullptr), mContainer(nullptr)
379 RDFContainerImpl::Init()
381 if (gRefCnt
++ == 0) {
384 rv
= CallGetService(kRDFServiceCID
, &gRDFService
);
386 NS_ERROR("unable to get RDF service");
390 rv
= gRDFService
->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI
"nextVal"),
392 if (NS_FAILED(rv
)) return rv
;
394 rv
= CallGetService(kRDFContainerUtilsCID
, &gRDFContainerUtils
);
396 NS_ERROR("unable to get RDF container utils service");
405 RDFContainerImpl::~RDFContainerImpl()
409 fprintf(stdout
, "%d - RDF: RDFContainerImpl\n", gInstanceCount
);
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
);
424 NS_NewRDFContainer(nsIRDFContainer
** aResult
)
426 RDFContainerImpl
* result
= new RDFContainerImpl();
428 return NS_ERROR_OUT_OF_MEMORY
;
444 NS_NewRDFContainer(nsIRDFDataSource
* aDataSource
,
445 nsIRDFResource
* aResource
,
446 nsIRDFContainer
** aResult
)
449 rv
= NS_NewRDFContainer(aResult
);
450 if (NS_FAILED(rv
)) return rv
;
452 rv
= (*aResult
)->Init(aDataSource
, aResource
);
454 NS_RELEASE(*aResult
);
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
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
490 rv
= SetNextValue(count
+ aIncrement
+ 1);
491 if (NS_FAILED(rv
)) return rv
;
495 if (aIncrement
< 0) {
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
);
507 propagatable
->SetPropagateChanges(false);
511 while (!err
&& ((aIncrement
< 0) ? (i
<= count
) : (i
>= aStartIndex
)))
513 nsCOMPtr
<nsIRDFResource
> oldOrdinal
;
514 rv
= gRDFContainerUtils
->IndexToOrdinalResource(i
, getter_AddRefs(oldOrdinal
));
521 nsCOMPtr
<nsIRDFResource
> newOrdinal
;
522 rv
= gRDFContainerUtils
->IndexToOrdinalResource(i
+ aIncrement
, getter_AddRefs(newOrdinal
));
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
));
545 rv
= targets
->HasMoreElements(&hasMore
);
555 nsCOMPtr
<nsISupports
> isupports
;
556 rv
= targets
->GetNext(getter_AddRefs(isupports
));
563 nsCOMPtr
<nsIRDFNode
> element( do_QueryInterface(isupports
) );
564 NS_ASSERTION(element
!= nullptr, "something funky in the enumerator");
568 rv
= NS_ERROR_UNEXPECTED
;
572 rv
= mDataSource
->Unassert(mContainer
, oldOrdinal
, element
);
579 rv
= mDataSource
->Assert(mContainer
, newOrdinal
, element
, true);
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);
603 // Note: MUST enable notifications before exiting this method
605 propagatable
->SetPropagateChanges(true);
616 RDFContainerImpl::SetNextValue(PRInt32 aIndex
)
618 if (!mDataSource
|| !mContainer
)
619 return NS_ERROR_NOT_INITIALIZED
;
623 // Remove the current value of nextVal, if there is one.
624 nsCOMPtr
<nsIRDFNode
> nextValNode
;
625 if (NS_SUCCEEDED(rv
= mDataSource
->GetTarget(mContainer
,
628 getter_AddRefs(nextValNode
)))) {
629 if (NS_FAILED(rv
= mDataSource
->Unassert(mContainer
, kRDF_nextVal
, nextValNode
))) {
630 NS_ERROR("unable to update nextVal");
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");
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
;
655 RDFContainerImpl::GetNextValue(nsIRDFResource
** aResult
)
657 if (!mDataSource
|| !mContainer
)
658 return NS_ERROR_NOT_INITIALIZED
;
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
;
676 rv
= nextValLiteral
->GetValueConst(&s
);
677 if (NS_FAILED(rv
)) return rv
;
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')
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
;
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
);
721 // ignore error; failure just means slower access
722 (void)inMem
->EnsureFastContainment(mContainer
);