1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
/* ***** BEGIN LICENSE BLOCK *****
3
* Version: NPL 1.1/GPL 2.0/LGPL 2.1
5
* The contents of this file are subject to the Netscape Public License
6
* Version 1.1 (the "License"); you may not use this file except in
7
* compliance with the License. You may obtain a copy of the License at
8
* http://www.mozilla.org/NPL/
10
* Software distributed under the License is distributed on an "AS IS" basis,
11
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12
* for the specific language governing rights and limitations under the
15
* The Original Code is Mozilla Communicator client code.
17
* The Initial Developer of the Original Code is
18
* Netscape Communications Corporation.
19
* Portions created by the Initial Developer are Copyright (C) 1998
20
* the Initial Developer. All Rights Reserved.
23
* Pierre Phaneuf <pp@ludusdesign.com>
25
* Alternatively, the contents of this file may be used under the terms of
26
* either the GNU General Public License Version 2 or later (the "GPL"), or
27
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28
* in which case the provisions of the GPL or the LGPL are applicable instead
29
* of those above. If you wish to allow use of your version of this file only
30
* under the terms of either the GPL or the LGPL, and not to allow others to
31
* use your version of this file under the terms of the NPL, indicate your
32
* decision by deleting the provisions above and replace them with the notice
33
* and other provisions required by the GPL or the LGPL. If you do not delete
34
* the provisions above, a recipient may use your version of this file under
35
* the terms of any one of the NPL, the GPL or the LGPL.
37
* ***** END LICENSE BLOCK ***** */
41
Implementation for the RDF container.
46
1. RDF containers are one-indexed. This means that a lot of the loops
47
that you'd normally think you'd write like this:
49
for (i = 0; i < count; ++i) {}
51
You've gotta write like this:
53
for (i = 1; i <= count; ++i) {}
55
"Sure, right, yeah, of course.", you say. Well maybe I'm just
56
thick, but it's easy to slip up.
58
2. The RDF:nextVal property on the container is an
59
implementation-level hack that is used to quickly compute the
60
next value for appending to the container. It will no doubt
61
become royally screwed up in the case of aggregation.
63
3. The RDF:nextVal property is also used to retrieve the count of
64
elements in the container.
70
#include "nsIRDFContainer.h"
71
#include "nsIRDFContainerUtils.h"
72
#include "nsIRDFInMemoryDataSource.h"
73
#include "nsIRDFPropagatableDataSource.h"
74
#include "nsIRDFService.h"
75
#include "nsIServiceManager.h"
78
#include "nsXPIDLString.h"
81
static NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID);
82
static NS_DEFINE_CID(kRDFContainerUtilsCID, NS_RDFCONTAINERUTILS_CID);
83
static const char kRDFNameSpaceURI[] = RDF_NAMESPACE_URI;
85
#define RDF_SEQ_LIST_LIMIT 8
87
class RDFContainerImpl : public nsIRDFContainer
91
// nsISupports interface
94
// nsIRDFContainer interface
95
NS_DECL_NSIRDFCONTAINER
98
friend nsresult NS_NewRDFContainer(nsIRDFContainer** aResult);
101
virtual ~RDFContainerImpl();
105
nsresult Renumber(PRInt32 aStartIndex, PRInt32 aIncrement);
106
nsresult SetNextValue(PRInt32 aIndex);
107
nsresult GetNextValue(nsIRDFResource** aResult);
109
nsIRDFDataSource* mDataSource;
110
nsIRDFResource* mContainer;
113
static PRInt32 gRefCnt;
114
static nsIRDFService* gRDFService;
115
static nsIRDFContainerUtils* gRDFContainerUtils;
116
static nsIRDFResource* kRDF_nextVal;
120
PRInt32 RDFContainerImpl::gRefCnt = 0;
121
nsIRDFService* RDFContainerImpl::gRDFService;
122
nsIRDFContainerUtils* RDFContainerImpl::gRDFContainerUtils;
123
nsIRDFResource* RDFContainerImpl::kRDF_nextVal;
125
////////////////////////////////////////////////////////////////////////
126
// nsISupports interface
128
NS_IMPL_ISUPPORTS1(RDFContainerImpl, nsIRDFContainer)
132
////////////////////////////////////////////////////////////////////////
133
// nsIRDFContainer interface
136
RDFContainerImpl::GetDataSource(nsIRDFDataSource** _retval)
138
*_retval = mDataSource;
139
NS_IF_ADDREF(*_retval);
145
RDFContainerImpl::GetResource(nsIRDFResource** _retval)
147
*_retval = mContainer;
148
NS_IF_ADDREF(*_retval);
154
RDFContainerImpl::Init(nsIRDFDataSource *aDataSource, nsIRDFResource *aContainer)
156
NS_PRECONDITION(aDataSource != nsnull, "null ptr");
158
return NS_ERROR_NULL_POINTER;
160
NS_PRECONDITION(aContainer != nsnull, "null ptr");
162
return NS_ERROR_NULL_POINTER;
166
rv = gRDFContainerUtils->IsContainer(aDataSource, aContainer, &isContainer);
167
if (NS_FAILED(rv)) return rv;
169
// ``throw'' if we can't create a container on the specified
170
// datasource/resource combination.
172
return NS_ERROR_FAILURE;
174
NS_IF_RELEASE(mDataSource);
175
mDataSource = aDataSource;
176
NS_ADDREF(mDataSource);
178
NS_IF_RELEASE(mContainer);
179
mContainer = aContainer;
180
NS_ADDREF(mContainer);
187
RDFContainerImpl::GetCount(PRInt32 *aCount)
189
if (!mDataSource || !mContainer)
190
return NS_ERROR_NOT_INITIALIZED;
194
// Get the next value, which hangs off of the bag via the
195
// RDF:nextVal property. This is the _next value_ that will get
196
// assigned in a one-indexed array. So, it's actually _one more_
197
// than the actual count of elements in the container.
199
// XXX To handle aggregation, this should probably be a
200
// GetTargets() that enumerates all of the values and picks the
202
nsCOMPtr<nsIRDFNode> nextValNode;
203
rv = mDataSource->GetTarget(mContainer, kRDF_nextVal, PR_TRUE, getter_AddRefs(nextValNode));
204
if (NS_FAILED(rv)) return rv;
206
if (rv == NS_RDF_NO_VALUE)
207
return NS_ERROR_UNEXPECTED;
209
nsCOMPtr<nsIRDFLiteral> nextValLiteral;
210
rv = nextValNode->QueryInterface(NS_GET_IID(nsIRDFLiteral), getter_AddRefs(nextValLiteral));
211
if (NS_FAILED(rv)) return rv;
214
rv = nextValLiteral->GetValueConst( &s );
215
if (NS_FAILED(rv)) return rv;
217
nsAutoString nextValStr(s);
221
nextVal = nextValStr.ToInteger(&err);
223
return NS_ERROR_UNEXPECTED;
225
*aCount = nextVal - 1;
231
RDFContainerImpl::GetElements(nsISimpleEnumerator **_retval)
233
if (!mDataSource || !mContainer)
234
return NS_ERROR_NOT_INITIALIZED;
236
return NS_NewContainerEnumerator(mDataSource, mContainer, _retval);
241
RDFContainerImpl::AppendElement(nsIRDFNode *aElement)
243
if (!mDataSource || !mContainer)
244
return NS_ERROR_NOT_INITIALIZED;
246
NS_PRECONDITION(aElement != nsnull, "null ptr");
248
return NS_ERROR_NULL_POINTER;
252
nsCOMPtr<nsIRDFResource> nextVal;
253
rv = GetNextValue(getter_AddRefs(nextVal));
254
if (NS_FAILED(rv)) return rv;
256
rv = mDataSource->Assert(mContainer, nextVal, aElement, PR_TRUE);
257
if (NS_FAILED(rv)) return rv;
264
RDFContainerImpl::RemoveElement(nsIRDFNode *aElement, PRBool aRenumber)
266
if (!mDataSource || !mContainer)
267
return NS_ERROR_NOT_INITIALIZED;
269
NS_PRECONDITION(aElement != nsnull, "null ptr");
271
return NS_ERROR_NULL_POINTER;
276
rv = IndexOf(aElement, &idx);
277
if (NS_FAILED(rv)) return rv;
282
// Remove the element.
283
nsCOMPtr<nsIRDFResource> ordinal;
284
rv = gRDFContainerUtils->IndexToOrdinalResource(idx,
285
getter_AddRefs(ordinal));
286
if (NS_FAILED(rv)) return rv;
288
rv = mDataSource->Unassert(mContainer, ordinal, aElement);
289
if (NS_FAILED(rv)) return rv;
292
// Now slide the rest of the collection backwards to fill in
293
// the gap. This will have the side effect of completely
294
// renumber the container from index to the end.
295
rv = Renumber(idx + 1, -1);
296
if (NS_FAILED(rv)) return rv;
304
RDFContainerImpl::InsertElementAt(nsIRDFNode *aElement, PRInt32 aIndex, PRBool aRenumber)
306
if (!mDataSource || !mContainer)
307
return NS_ERROR_NOT_INITIALIZED;
309
NS_PRECONDITION(aElement != nsnull, "null ptr");
311
return NS_ERROR_NULL_POINTER;
313
NS_PRECONDITION(aIndex >= 1, "illegal value");
315
return NS_ERROR_ILLEGAL_VALUE;
320
rv = GetCount(&count);
321
if (NS_FAILED(rv)) return rv;
323
NS_ASSERTION(aIndex <= count + 1, "illegal value");
324
if (aIndex > count + 1)
325
return NS_ERROR_ILLEGAL_VALUE;
328
// Make a hole for the element. This will have the side effect of
329
// completely renumbering the container from 'aIndex' to 'count',
330
// and will spew assertions.
331
rv = Renumber(aIndex, +1);
332
if (NS_FAILED(rv)) return rv;
335
nsCOMPtr<nsIRDFResource> ordinal;
336
rv = gRDFContainerUtils->IndexToOrdinalResource(aIndex, getter_AddRefs(ordinal));
337
if (NS_FAILED(rv)) return rv;
339
rv = mDataSource->Assert(mContainer, ordinal, aElement, PR_TRUE);
340
if (NS_FAILED(rv)) return rv;
346
RDFContainerImpl::RemoveElementAt(PRInt32 aIndex, PRBool aRenumber, nsIRDFNode** _retval)
348
if (!mDataSource || !mContainer)
349
return NS_ERROR_NOT_INITIALIZED;
354
return NS_ERROR_ILLEGAL_VALUE;
359
rv = GetCount(&count);
360
if (NS_FAILED(rv)) return rv;
363
return NS_ERROR_ILLEGAL_VALUE;
365
nsCOMPtr<nsIRDFResource> ordinal;
366
rv = gRDFContainerUtils->IndexToOrdinalResource(aIndex, getter_AddRefs(ordinal));
367
if (NS_FAILED(rv)) return rv;
369
nsCOMPtr<nsIRDFNode> old;
370
rv = mDataSource->GetTarget(mContainer, ordinal, PR_TRUE, getter_AddRefs(old));
371
if (NS_FAILED(rv)) return rv;
374
rv = mDataSource->Unassert(mContainer, ordinal, old);
375
if (NS_FAILED(rv)) return rv;
378
// Now slide the rest of the collection backwards to fill in
379
// the gap. This will have the side effect of completely
380
// renumber the container from index to the end.
381
rv = Renumber(aIndex + 1, -1);
382
if (NS_FAILED(rv)) return rv;
393
RDFContainerImpl::IndexOf(nsIRDFNode *aElement, PRInt32 *aIndex)
395
if (!mDataSource || !mContainer)
396
return NS_ERROR_NOT_INITIALIZED;
398
return gRDFContainerUtils->IndexOf(mDataSource, mContainer,
403
////////////////////////////////////////////////////////////////////////
406
RDFContainerImpl::RDFContainerImpl()
407
: mDataSource(nsnull), mContainer(nsnull)
413
RDFContainerImpl::Init()
415
if (gRefCnt++ == 0) {
418
rv = nsServiceManager::GetService(kRDFServiceCID,
419
NS_GET_IID(nsIRDFService),
420
(nsISupports**) &gRDFService);
422
NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get RDF service");
423
if (NS_FAILED(rv)) return rv;
425
rv = gRDFService->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "nextVal"),
427
if (NS_FAILED(rv)) return rv;
429
rv = nsServiceManager::GetService(kRDFContainerUtilsCID,
430
NS_GET_IID(nsIRDFContainerUtils),
431
(nsISupports**) &gRDFContainerUtils);
433
NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get RDF container utils service");
434
if (NS_FAILED(rv)) return rv;
441
RDFContainerImpl::~RDFContainerImpl()
445
fprintf(stdout, "%d - RDF: RDFContainerImpl\n", gInstanceCount);
448
NS_IF_RELEASE(mContainer);
449
NS_IF_RELEASE(mDataSource);
451
if (--gRefCnt == 0) {
452
if (gRDFContainerUtils) {
453
nsServiceManager::ReleaseService(kRDFContainerUtilsCID, gRDFContainerUtils);
454
gRDFContainerUtils = nsnull;
458
nsServiceManager::ReleaseService(kRDFServiceCID, gRDFService);
459
gRDFService = nsnull;
462
NS_IF_RELEASE(kRDF_nextVal);
468
NS_NewRDFContainer(nsIRDFContainer** aResult)
470
RDFContainerImpl* result = new RDFContainerImpl();
472
return NS_ERROR_OUT_OF_MEMORY;
488
NS_NewRDFContainer(nsIRDFDataSource* aDataSource,
489
nsIRDFResource* aResource,
490
nsIRDFContainer** aResult)
493
rv = NS_NewRDFContainer(aResult);
494
if (NS_FAILED(rv)) return rv;
496
rv = (*aResult)->Init(aDataSource, aResource);
498
NS_RELEASE(*aResult);
505
RDFContainerImpl::Renumber(PRInt32 aStartIndex, PRInt32 aIncrement)
507
if (!mDataSource || !mContainer)
508
return NS_ERROR_NOT_INITIALIZED;
510
// Renumber the elements in the container starting with
511
// aStartIndex, updating each element's index by aIncrement. For
526
rv = GetCount(&count);
527
if (NS_FAILED(rv)) return rv;
529
if (aIncrement > 0) {
530
// Update the container's nextVal to reflect the
531
// renumbering. We do this now if aIncrement > 0 because we'll
532
// want to be able to acknowledge that new elements are in the
534
rv = SetNextValue(count + aIncrement + 1);
535
if (NS_FAILED(rv)) return rv;
539
if (aIncrement < 0) {
543
i = count; // we're one-indexed.
546
// Note: once we disable notifications, don't exit this method until
547
// enabling notifications
548
nsCOMPtr<nsIRDFPropagatableDataSource> propagatable =
549
do_QueryInterface(mDataSource);
551
propagatable->SetPropagateChanges(PR_FALSE);
554
PRBool err = PR_FALSE;
555
while ((err == PR_FALSE) && ((aIncrement < 0) ? (i <= count) : (i >= aStartIndex)))
557
nsCOMPtr<nsIRDFResource> oldOrdinal;
558
rv = gRDFContainerUtils->IndexToOrdinalResource(i, getter_AddRefs(oldOrdinal));
565
nsCOMPtr<nsIRDFResource> newOrdinal;
566
rv = gRDFContainerUtils->IndexToOrdinalResource(i + aIncrement, getter_AddRefs(newOrdinal));
573
// Because of aggregation, we need to be paranoid about the
574
// possibility that >1 element may be present per ordinal. If
575
// there _is_ in fact more than one element, they'll all get
576
// assigned to the same new ordinal; i.e., we don't make any
577
// attempt to "clean up" the duplicate numbering. (Doing so
578
// would require two passes.)
579
nsCOMPtr<nsISimpleEnumerator> targets;
580
rv = mDataSource->GetTargets(mContainer, oldOrdinal, PR_TRUE, getter_AddRefs(targets));
589
rv = targets->HasMoreElements(&hasMore);
599
nsCOMPtr<nsISupports> isupports;
600
rv = targets->GetNext(getter_AddRefs(isupports));
607
nsCOMPtr<nsIRDFNode> element( do_QueryInterface(isupports) );
608
NS_ASSERTION(element != nsnull, "something funky in the enumerator");
612
rv = NS_ERROR_UNEXPECTED;
616
rv = mDataSource->Unassert(mContainer, oldOrdinal, element);
623
rv = mDataSource->Assert(mContainer, newOrdinal, element, PR_TRUE);
634
if ((err == PR_FALSE) && (aIncrement < 0))
636
// Update the container's nextVal to reflect the
637
// renumbering. We do this now if aIncrement < 0 because, up
638
// until this point, we'll want people to be able to find
639
// things that are still "at the end".
640
rv = SetNextValue(count + aIncrement + 1);
647
// Note: MUST enable notifications before exiting this method
649
propagatable->SetPropagateChanges(PR_TRUE);
652
if (err == PR_TRUE) return(rv);
660
RDFContainerImpl::SetNextValue(PRInt32 aIndex)
662
if (!mDataSource || !mContainer)
663
return NS_ERROR_NOT_INITIALIZED;
667
// Remove the current value of nextVal, if there is one.
668
nsCOMPtr<nsIRDFNode> nextValNode;
669
if (NS_SUCCEEDED(rv = mDataSource->GetTarget(mContainer,
672
getter_AddRefs(nextValNode)))) {
673
if (NS_FAILED(rv = mDataSource->Unassert(mContainer, kRDF_nextVal, nextValNode))) {
674
NS_ERROR("unable to update nextVal");
680
s.AppendInt(aIndex, 10);
682
nsCOMPtr<nsIRDFLiteral> nextVal;
683
if (NS_FAILED(rv = gRDFService->GetLiteral(s.get(), getter_AddRefs(nextVal)))) {
684
NS_ERROR("unable to get nextVal literal");
688
rv = mDataSource->Assert(mContainer, kRDF_nextVal, nextVal, PR_TRUE);
689
if (rv != NS_RDF_ASSERTION_ACCEPTED) {
690
NS_ERROR("unable to update nextVal");
691
return NS_ERROR_FAILURE;
699
RDFContainerImpl::GetNextValue(nsIRDFResource** aResult)
701
if (!mDataSource || !mContainer)
702
return NS_ERROR_NOT_INITIALIZED;
706
// Get the next value, which hangs off of the bag via the
707
// RDF:nextVal property.
708
nsCOMPtr<nsIRDFNode> nextValNode;
709
rv = mDataSource->GetTarget(mContainer, kRDF_nextVal, PR_TRUE, getter_AddRefs(nextValNode));
710
if (NS_FAILED(rv)) return rv;
712
if (rv == NS_RDF_NO_VALUE)
713
return NS_ERROR_UNEXPECTED;
715
nsCOMPtr<nsIRDFLiteral> nextValLiteral;
716
rv = nextValNode->QueryInterface(NS_GET_IID(nsIRDFLiteral), getter_AddRefs(nextValLiteral));
717
if (NS_FAILED(rv)) return rv;
720
rv = nextValLiteral->GetValueConst(&s);
721
if (NS_FAILED(rv)) return rv;
725
for (const PRUnichar* p = s; *p != 0; ++p) {
726
NS_ASSERTION(*p >= '0' && *p <= '9', "not a digit");
727
if (*p < '0' || *p > '9')
735
char buf[sizeof(kRDFNameSpaceURI) + 16];
736
nsFixedCString nextValStr(buf, sizeof(buf), 0);
737
nextValStr = kRDFNameSpaceURI;
738
nextValStr.Append("_");
739
nextValStr.AppendInt(nextVal, 10);
741
rv = gRDFService->GetResource(nextValStr, aResult);
742
if (NS_FAILED(rv)) return rv;
744
// Now increment the RDF:nextVal property.
745
rv = mDataSource->Unassert(mContainer, kRDF_nextVal, nextValLiteral);
746
if (NS_FAILED(rv)) return rv;
749
nextValStr.Truncate();
750
nextValStr.AppendInt(nextVal, 10);
752
rv = gRDFService->GetLiteral(NS_ConvertASCIItoUCS2(nextValStr).get(), getter_AddRefs(nextValLiteral));
753
if (NS_FAILED(rv)) return rv;
755
rv = mDataSource->Assert(mContainer, kRDF_nextVal, nextValLiteral, PR_TRUE);
756
if (NS_FAILED(rv)) return rv;
758
if (RDF_SEQ_LIST_LIMIT == nextVal)
760
// focal point for RDF container mutation;
761
// basically, provide a hint to allow for fast access
762
nsCOMPtr<nsIRDFInMemoryDataSource> inMem = do_QueryInterface(mDataSource);
765
// ignore error; failure just means slower access
766
(void)inMem->EnsureFastContainment(mContainer);