1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
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
13 : * License.
14 : *
15 : * The Original Code is Mozilla Communicator client code.
16 : *
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.
21 : *
22 : * Contributor(s):
23 : * Pierre Phaneuf <pp@ludusdesign.com>
24 : *
25 : * Alternatively, the contents of this file may be used under the terms of
26 : * either of the GNU General Public License Version 2 or later (the "GPL"),
27 : * or 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 MPL, 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 MPL, the GPL or the LGPL.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 :
39 : /*
40 :
41 : Implementation for the RDF container.
42 :
43 : Notes
44 : -----
45 :
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:
48 :
49 : for (i = 0; i < count; ++i) {}
50 :
51 : You've gotta write like this:
52 :
53 : for (i = 1; i <= count; ++i) {}
54 :
55 : "Sure, right, yeah, of course.", you say. Well maybe I'm just
56 : thick, but it's easy to slip up.
57 :
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.
62 :
63 : 3. The RDF:nextVal property is also used to retrieve the count of
64 : elements in the container.
65 :
66 : */
67 :
68 :
69 : #include "nsCOMPtr.h"
70 : #include "nsIRDFContainer.h"
71 : #include "nsIRDFContainerUtils.h"
72 : #include "nsIRDFInMemoryDataSource.h"
73 : #include "nsIRDFPropagatableDataSource.h"
74 : #include "nsIRDFService.h"
75 : #include "nsIServiceManager.h"
76 : #include "nsRDFCID.h"
77 : #include "nsString.h"
78 : #include "nsXPIDLString.h"
79 : #include "rdf.h"
80 :
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;
84 :
85 : #define RDF_SEQ_LIST_LIMIT 8
86 :
87 : class RDFContainerImpl : public nsIRDFContainer
88 : {
89 : public:
90 :
91 : // nsISupports interface
92 : NS_DECL_ISUPPORTS
93 :
94 : // nsIRDFContainer interface
95 : NS_DECL_NSIRDFCONTAINER
96 :
97 : private:
98 : friend nsresult NS_NewRDFContainer(nsIRDFContainer** aResult);
99 :
100 : RDFContainerImpl();
101 : virtual ~RDFContainerImpl();
102 :
103 : nsresult Init();
104 :
105 : nsresult Renumber(PRInt32 aStartIndex, PRInt32 aIncrement);
106 : nsresult SetNextValue(PRInt32 aIndex);
107 : nsresult GetNextValue(nsIRDFResource** aResult);
108 :
109 : nsIRDFDataSource* mDataSource;
110 : nsIRDFResource* mContainer;
111 :
112 : // pseudo constants
113 : static PRInt32 gRefCnt;
114 : static nsIRDFService* gRDFService;
115 : static nsIRDFContainerUtils* gRDFContainerUtils;
116 : static nsIRDFResource* kRDF_nextVal;
117 : };
118 :
119 :
120 : PRInt32 RDFContainerImpl::gRefCnt = 0;
121 : nsIRDFService* RDFContainerImpl::gRDFService;
122 : nsIRDFContainerUtils* RDFContainerImpl::gRDFContainerUtils;
123 : nsIRDFResource* RDFContainerImpl::kRDF_nextVal;
124 :
125 : ////////////////////////////////////////////////////////////////////////
126 : // nsISupports interface
127 :
128 14811 : NS_IMPL_ISUPPORTS1(RDFContainerImpl, nsIRDFContainer)
129 :
130 :
131 :
132 : ////////////////////////////////////////////////////////////////////////
133 : // nsIRDFContainer interface
134 :
135 : NS_IMETHODIMP
136 0 : RDFContainerImpl::GetDataSource(nsIRDFDataSource** _retval)
137 : {
138 0 : *_retval = mDataSource;
139 0 : NS_IF_ADDREF(*_retval);
140 0 : return NS_OK;
141 : }
142 :
143 :
144 : NS_IMETHODIMP
145 11 : RDFContainerImpl::GetResource(nsIRDFResource** _retval)
146 : {
147 11 : *_retval = mContainer;
148 11 : NS_IF_ADDREF(*_retval);
149 11 : return NS_OK;
150 : }
151 :
152 :
153 : NS_IMETHODIMP
154 2288 : RDFContainerImpl::Init(nsIRDFDataSource *aDataSource, nsIRDFResource *aContainer)
155 : {
156 2288 : NS_PRECONDITION(aDataSource != nsnull, "null ptr");
157 2288 : if (! aDataSource)
158 0 : return NS_ERROR_NULL_POINTER;
159 :
160 2288 : NS_PRECONDITION(aContainer != nsnull, "null ptr");
161 2288 : if (! aContainer)
162 0 : return NS_ERROR_NULL_POINTER;
163 :
164 : nsresult rv;
165 : bool isContainer;
166 2288 : rv = gRDFContainerUtils->IsContainer(aDataSource, aContainer, &isContainer);
167 2288 : if (NS_FAILED(rv)) return rv;
168 :
169 : // ``throw'' if we can't create a container on the specified
170 : // datasource/resource combination.
171 2288 : if (! isContainer)
172 0 : return NS_ERROR_FAILURE;
173 :
174 2288 : NS_IF_RELEASE(mDataSource);
175 2288 : mDataSource = aDataSource;
176 2288 : NS_ADDREF(mDataSource);
177 :
178 2288 : NS_IF_RELEASE(mContainer);
179 2288 : mContainer = aContainer;
180 2288 : NS_ADDREF(mContainer);
181 :
182 2288 : return NS_OK;
183 : }
184 :
185 :
186 : NS_IMETHODIMP
187 2 : RDFContainerImpl::GetCount(PRInt32 *aCount)
188 : {
189 2 : if (!mDataSource || !mContainer)
190 0 : return NS_ERROR_NOT_INITIALIZED;
191 :
192 : nsresult rv;
193 :
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.
198 : //
199 : // XXX To handle aggregation, this should probably be a
200 : // GetTargets() that enumerates all of the values and picks the
201 : // largest one.
202 4 : nsCOMPtr<nsIRDFNode> nextValNode;
203 2 : rv = mDataSource->GetTarget(mContainer, kRDF_nextVal, true, getter_AddRefs(nextValNode));
204 2 : if (NS_FAILED(rv)) return rv;
205 :
206 2 : if (rv == NS_RDF_NO_VALUE)
207 0 : return NS_ERROR_UNEXPECTED;
208 :
209 4 : nsCOMPtr<nsIRDFLiteral> nextValLiteral;
210 2 : rv = nextValNode->QueryInterface(NS_GET_IID(nsIRDFLiteral), getter_AddRefs(nextValLiteral));
211 2 : if (NS_FAILED(rv)) return rv;
212 :
213 : const PRUnichar *s;
214 2 : rv = nextValLiteral->GetValueConst( &s );
215 2 : if (NS_FAILED(rv)) return rv;
216 :
217 4 : nsAutoString nextValStr(s);
218 :
219 : PRInt32 nextVal;
220 : PRInt32 err;
221 2 : nextVal = nextValStr.ToInteger(&err);
222 2 : if (NS_FAILED(err))
223 0 : return NS_ERROR_UNEXPECTED;
224 :
225 2 : *aCount = nextVal - 1;
226 2 : return NS_OK;
227 : }
228 :
229 :
230 : NS_IMETHODIMP
231 262 : RDFContainerImpl::GetElements(nsISimpleEnumerator **_retval)
232 : {
233 262 : if (!mDataSource || !mContainer)
234 0 : return NS_ERROR_NOT_INITIALIZED;
235 :
236 262 : return NS_NewContainerEnumerator(mDataSource, mContainer, _retval);
237 : }
238 :
239 :
240 : NS_IMETHODIMP
241 2018 : RDFContainerImpl::AppendElement(nsIRDFNode *aElement)
242 : {
243 2018 : if (!mDataSource || !mContainer)
244 0 : return NS_ERROR_NOT_INITIALIZED;
245 :
246 2018 : NS_PRECONDITION(aElement != nsnull, "null ptr");
247 2018 : if (! aElement)
248 0 : return NS_ERROR_NULL_POINTER;
249 :
250 : nsresult rv;
251 :
252 4036 : nsCOMPtr<nsIRDFResource> nextVal;
253 2018 : rv = GetNextValue(getter_AddRefs(nextVal));
254 2018 : if (NS_FAILED(rv)) return rv;
255 :
256 2018 : rv = mDataSource->Assert(mContainer, nextVal, aElement, true);
257 2018 : if (NS_FAILED(rv)) return rv;
258 :
259 2018 : return NS_OK;
260 : }
261 :
262 :
263 : NS_IMETHODIMP
264 0 : RDFContainerImpl::RemoveElement(nsIRDFNode *aElement, bool aRenumber)
265 : {
266 0 : if (!mDataSource || !mContainer)
267 0 : return NS_ERROR_NOT_INITIALIZED;
268 :
269 0 : NS_PRECONDITION(aElement != nsnull, "null ptr");
270 0 : if (! aElement)
271 0 : return NS_ERROR_NULL_POINTER;
272 :
273 : nsresult rv;
274 :
275 : PRInt32 idx;
276 0 : rv = IndexOf(aElement, &idx);
277 0 : if (NS_FAILED(rv)) return rv;
278 :
279 0 : if (idx < 0)
280 0 : return NS_OK;
281 :
282 : // Remove the element.
283 0 : nsCOMPtr<nsIRDFResource> ordinal;
284 : rv = gRDFContainerUtils->IndexToOrdinalResource(idx,
285 0 : getter_AddRefs(ordinal));
286 0 : if (NS_FAILED(rv)) return rv;
287 :
288 0 : rv = mDataSource->Unassert(mContainer, ordinal, aElement);
289 0 : if (NS_FAILED(rv)) return rv;
290 :
291 0 : if (aRenumber) {
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 0 : rv = Renumber(idx + 1, -1);
296 0 : if (NS_FAILED(rv)) return rv;
297 : }
298 :
299 0 : return NS_OK;
300 : }
301 :
302 :
303 : NS_IMETHODIMP
304 0 : RDFContainerImpl::InsertElementAt(nsIRDFNode *aElement, PRInt32 aIndex, bool aRenumber)
305 : {
306 0 : if (!mDataSource || !mContainer)
307 0 : return NS_ERROR_NOT_INITIALIZED;
308 :
309 0 : NS_PRECONDITION(aElement != nsnull, "null ptr");
310 0 : if (! aElement)
311 0 : return NS_ERROR_NULL_POINTER;
312 :
313 0 : NS_PRECONDITION(aIndex >= 1, "illegal value");
314 0 : if (aIndex < 1)
315 0 : return NS_ERROR_ILLEGAL_VALUE;
316 :
317 : nsresult rv;
318 :
319 : PRInt32 count;
320 0 : rv = GetCount(&count);
321 0 : if (NS_FAILED(rv)) return rv;
322 :
323 0 : NS_ASSERTION(aIndex <= count + 1, "illegal value");
324 0 : if (aIndex > count + 1)
325 0 : return NS_ERROR_ILLEGAL_VALUE;
326 :
327 0 : if (aRenumber) {
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 0 : rv = Renumber(aIndex, +1);
332 0 : if (NS_FAILED(rv)) return rv;
333 : }
334 :
335 0 : nsCOMPtr<nsIRDFResource> ordinal;
336 0 : rv = gRDFContainerUtils->IndexToOrdinalResource(aIndex, getter_AddRefs(ordinal));
337 0 : if (NS_FAILED(rv)) return rv;
338 :
339 0 : rv = mDataSource->Assert(mContainer, ordinal, aElement, true);
340 0 : if (NS_FAILED(rv)) return rv;
341 :
342 0 : return NS_OK;
343 : }
344 :
345 : NS_IMETHODIMP
346 1 : RDFContainerImpl::RemoveElementAt(PRInt32 aIndex, bool aRenumber, nsIRDFNode** _retval)
347 : {
348 1 : if (!mDataSource || !mContainer)
349 0 : return NS_ERROR_NOT_INITIALIZED;
350 :
351 1 : *_retval = nsnull;
352 :
353 1 : if (aIndex< 1)
354 0 : return NS_ERROR_ILLEGAL_VALUE;
355 :
356 : nsresult rv;
357 :
358 : PRInt32 count;
359 1 : rv = GetCount(&count);
360 1 : if (NS_FAILED(rv)) return rv;
361 :
362 1 : if (aIndex > count)
363 0 : return NS_ERROR_ILLEGAL_VALUE;
364 :
365 2 : nsCOMPtr<nsIRDFResource> ordinal;
366 1 : rv = gRDFContainerUtils->IndexToOrdinalResource(aIndex, getter_AddRefs(ordinal));
367 1 : if (NS_FAILED(rv)) return rv;
368 :
369 2 : nsCOMPtr<nsIRDFNode> old;
370 1 : rv = mDataSource->GetTarget(mContainer, ordinal, true, getter_AddRefs(old));
371 1 : if (NS_FAILED(rv)) return rv;
372 :
373 1 : if (rv == NS_OK) {
374 1 : rv = mDataSource->Unassert(mContainer, ordinal, old);
375 1 : if (NS_FAILED(rv)) return rv;
376 :
377 1 : if (aRenumber) {
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 1 : rv = Renumber(aIndex + 1, -1);
382 1 : if (NS_FAILED(rv)) return rv;
383 : }
384 : }
385 :
386 1 : old.swap(*_retval);
387 :
388 1 : return NS_OK;
389 : }
390 :
391 : NS_IMETHODIMP
392 19 : RDFContainerImpl::IndexOf(nsIRDFNode *aElement, PRInt32 *aIndex)
393 : {
394 19 : if (!mDataSource || !mContainer)
395 0 : return NS_ERROR_NOT_INITIALIZED;
396 :
397 : return gRDFContainerUtils->IndexOf(mDataSource, mContainer,
398 19 : aElement, aIndex);
399 : }
400 :
401 :
402 : ////////////////////////////////////////////////////////////////////////
403 :
404 :
405 2288 : RDFContainerImpl::RDFContainerImpl()
406 2288 : : mDataSource(nsnull), mContainer(nsnull)
407 : {
408 2288 : }
409 :
410 :
411 : nsresult
412 2288 : RDFContainerImpl::Init()
413 : {
414 2288 : if (gRefCnt++ == 0) {
415 : nsresult rv;
416 :
417 2252 : rv = CallGetService(kRDFServiceCID, &gRDFService);
418 2252 : if (NS_FAILED(rv)) {
419 0 : NS_ERROR("unable to get RDF service");
420 0 : return rv;
421 : }
422 :
423 2252 : rv = gRDFService->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "nextVal"),
424 2252 : &kRDF_nextVal);
425 2252 : if (NS_FAILED(rv)) return rv;
426 :
427 2252 : rv = CallGetService(kRDFContainerUtilsCID, &gRDFContainerUtils);
428 2252 : if (NS_FAILED(rv)) {
429 0 : NS_ERROR("unable to get RDF container utils service");
430 0 : return rv;
431 : }
432 : }
433 :
434 2288 : return NS_OK;
435 : }
436 :
437 :
438 4576 : RDFContainerImpl::~RDFContainerImpl()
439 : {
440 : #ifdef DEBUG_REFS
441 : --gInstanceCount;
442 : fprintf(stdout, "%d - RDF: RDFContainerImpl\n", gInstanceCount);
443 : #endif
444 :
445 2288 : NS_IF_RELEASE(mContainer);
446 2288 : NS_IF_RELEASE(mDataSource);
447 :
448 2288 : if (--gRefCnt == 0) {
449 2252 : NS_IF_RELEASE(gRDFContainerUtils);
450 2252 : NS_IF_RELEASE(gRDFService);
451 2252 : NS_IF_RELEASE(kRDF_nextVal);
452 : }
453 9152 : }
454 :
455 :
456 : nsresult
457 2288 : NS_NewRDFContainer(nsIRDFContainer** aResult)
458 : {
459 2288 : RDFContainerImpl* result = new RDFContainerImpl();
460 2288 : if (! result)
461 0 : return NS_ERROR_OUT_OF_MEMORY;
462 :
463 : nsresult rv;
464 2288 : rv = result->Init();
465 2288 : if (NS_FAILED(rv)) {
466 0 : delete result;
467 0 : return rv;
468 : }
469 :
470 2288 : NS_ADDREF(result);
471 2288 : *aResult = result;
472 2288 : return NS_OK;
473 : }
474 :
475 :
476 : nsresult
477 0 : NS_NewRDFContainer(nsIRDFDataSource* aDataSource,
478 : nsIRDFResource* aResource,
479 : nsIRDFContainer** aResult)
480 : {
481 : nsresult rv;
482 0 : rv = NS_NewRDFContainer(aResult);
483 0 : if (NS_FAILED(rv)) return rv;
484 :
485 0 : rv = (*aResult)->Init(aDataSource, aResource);
486 0 : if (NS_FAILED(rv)) {
487 0 : NS_RELEASE(*aResult);
488 : }
489 0 : return rv;
490 : }
491 :
492 :
493 : nsresult
494 1 : RDFContainerImpl::Renumber(PRInt32 aStartIndex, PRInt32 aIncrement)
495 : {
496 1 : if (!mDataSource || !mContainer)
497 0 : return NS_ERROR_NOT_INITIALIZED;
498 :
499 : // Renumber the elements in the container starting with
500 : // aStartIndex, updating each element's index by aIncrement. For
501 : // example,
502 : //
503 : // (1:a 2:b 3:c)
504 : // Renumber(2, +1);
505 : // (1:a 3:b 4:c)
506 : // Renumber(3, -1);
507 : // (1:a 2:b 3:c)
508 : //
509 : nsresult rv;
510 :
511 1 : if (! aIncrement)
512 0 : return NS_OK;
513 :
514 : PRInt32 count;
515 1 : rv = GetCount(&count);
516 1 : if (NS_FAILED(rv)) return rv;
517 :
518 1 : if (aIncrement > 0) {
519 : // Update the container's nextVal to reflect the
520 : // renumbering. We do this now if aIncrement > 0 because we'll
521 : // want to be able to acknowledge that new elements are in the
522 : // container.
523 0 : rv = SetNextValue(count + aIncrement + 1);
524 0 : if (NS_FAILED(rv)) return rv;
525 : }
526 :
527 : PRInt32 i;
528 1 : if (aIncrement < 0) {
529 1 : i = aStartIndex;
530 : }
531 : else {
532 0 : i = count; // we're one-indexed.
533 : }
534 :
535 : // Note: once we disable notifications, don't exit this method until
536 : // enabling notifications
537 : nsCOMPtr<nsIRDFPropagatableDataSource> propagatable =
538 2 : do_QueryInterface(mDataSource);
539 1 : if (propagatable) {
540 0 : propagatable->SetPropagateChanges(false);
541 : }
542 :
543 1 : bool err = false;
544 2 : while (!err && ((aIncrement < 0) ? (i <= count) : (i >= aStartIndex)))
545 : {
546 0 : nsCOMPtr<nsIRDFResource> oldOrdinal;
547 0 : rv = gRDFContainerUtils->IndexToOrdinalResource(i, getter_AddRefs(oldOrdinal));
548 0 : if (NS_FAILED(rv))
549 : {
550 0 : err = true;
551 0 : continue;
552 : }
553 :
554 0 : nsCOMPtr<nsIRDFResource> newOrdinal;
555 0 : rv = gRDFContainerUtils->IndexToOrdinalResource(i + aIncrement, getter_AddRefs(newOrdinal));
556 0 : if (NS_FAILED(rv))
557 : {
558 0 : err = true;
559 0 : continue;
560 : }
561 :
562 : // Because of aggregation, we need to be paranoid about the
563 : // possibility that >1 element may be present per ordinal. If
564 : // there _is_ in fact more than one element, they'll all get
565 : // assigned to the same new ordinal; i.e., we don't make any
566 : // attempt to "clean up" the duplicate numbering. (Doing so
567 : // would require two passes.)
568 0 : nsCOMPtr<nsISimpleEnumerator> targets;
569 0 : rv = mDataSource->GetTargets(mContainer, oldOrdinal, true, getter_AddRefs(targets));
570 0 : if (NS_FAILED(rv))
571 : {
572 0 : err = true;
573 0 : continue;
574 : }
575 :
576 0 : while (1) {
577 : bool hasMore;
578 0 : rv = targets->HasMoreElements(&hasMore);
579 0 : if (NS_FAILED(rv))
580 : {
581 0 : err = true;
582 0 : break;
583 : }
584 :
585 0 : if (! hasMore)
586 0 : break;
587 :
588 0 : nsCOMPtr<nsISupports> isupports;
589 0 : rv = targets->GetNext(getter_AddRefs(isupports));
590 0 : if (NS_FAILED(rv))
591 : {
592 0 : err = true;
593 : break;
594 : }
595 :
596 0 : nsCOMPtr<nsIRDFNode> element( do_QueryInterface(isupports) );
597 0 : NS_ASSERTION(element != nsnull, "something funky in the enumerator");
598 0 : if (! element)
599 : {
600 0 : err = true;
601 0 : rv = NS_ERROR_UNEXPECTED;
602 : break;
603 : }
604 :
605 0 : rv = mDataSource->Unassert(mContainer, oldOrdinal, element);
606 0 : if (NS_FAILED(rv))
607 : {
608 0 : err = true;
609 : break;
610 : }
611 :
612 0 : rv = mDataSource->Assert(mContainer, newOrdinal, element, true);
613 0 : if (NS_FAILED(rv))
614 : {
615 0 : err = true;
616 : break;
617 : }
618 : }
619 :
620 0 : i -= aIncrement;
621 : }
622 :
623 1 : if (!err && (aIncrement < 0))
624 : {
625 : // Update the container's nextVal to reflect the
626 : // renumbering. We do this now if aIncrement < 0 because, up
627 : // until this point, we'll want people to be able to find
628 : // things that are still "at the end".
629 1 : rv = SetNextValue(count + aIncrement + 1);
630 1 : if (NS_FAILED(rv))
631 : {
632 0 : err = true;
633 : }
634 : }
635 :
636 : // Note: MUST enable notifications before exiting this method
637 1 : if (propagatable) {
638 0 : propagatable->SetPropagateChanges(true);
639 : }
640 :
641 1 : if (err) return(rv);
642 :
643 1 : return NS_OK;
644 : }
645 :
646 :
647 :
648 : nsresult
649 1 : RDFContainerImpl::SetNextValue(PRInt32 aIndex)
650 : {
651 1 : if (!mDataSource || !mContainer)
652 0 : return NS_ERROR_NOT_INITIALIZED;
653 :
654 : nsresult rv;
655 :
656 : // Remove the current value of nextVal, if there is one.
657 2 : nsCOMPtr<nsIRDFNode> nextValNode;
658 1 : if (NS_SUCCEEDED(rv = mDataSource->GetTarget(mContainer,
659 : kRDF_nextVal,
660 : true,
661 : getter_AddRefs(nextValNode)))) {
662 1 : if (NS_FAILED(rv = mDataSource->Unassert(mContainer, kRDF_nextVal, nextValNode))) {
663 0 : NS_ERROR("unable to update nextVal");
664 0 : return rv;
665 : }
666 : }
667 :
668 2 : nsAutoString s;
669 1 : s.AppendInt(aIndex, 10);
670 :
671 2 : nsCOMPtr<nsIRDFLiteral> nextVal;
672 1 : if (NS_FAILED(rv = gRDFService->GetLiteral(s.get(), getter_AddRefs(nextVal)))) {
673 0 : NS_ERROR("unable to get nextVal literal");
674 0 : return rv;
675 : }
676 :
677 1 : rv = mDataSource->Assert(mContainer, kRDF_nextVal, nextVal, true);
678 1 : if (rv != NS_RDF_ASSERTION_ACCEPTED) {
679 0 : NS_ERROR("unable to update nextVal");
680 0 : return NS_ERROR_FAILURE;
681 : }
682 :
683 1 : return NS_OK;
684 : }
685 :
686 :
687 : nsresult
688 2018 : RDFContainerImpl::GetNextValue(nsIRDFResource** aResult)
689 : {
690 2018 : if (!mDataSource || !mContainer)
691 0 : return NS_ERROR_NOT_INITIALIZED;
692 :
693 : nsresult rv;
694 :
695 : // Get the next value, which hangs off of the bag via the
696 : // RDF:nextVal property.
697 4036 : nsCOMPtr<nsIRDFNode> nextValNode;
698 2018 : rv = mDataSource->GetTarget(mContainer, kRDF_nextVal, true, getter_AddRefs(nextValNode));
699 2018 : if (NS_FAILED(rv)) return rv;
700 :
701 2018 : if (rv == NS_RDF_NO_VALUE)
702 0 : return NS_ERROR_UNEXPECTED;
703 :
704 4036 : nsCOMPtr<nsIRDFLiteral> nextValLiteral;
705 2018 : rv = nextValNode->QueryInterface(NS_GET_IID(nsIRDFLiteral), getter_AddRefs(nextValLiteral));
706 2018 : if (NS_FAILED(rv)) return rv;
707 :
708 : const PRUnichar* s;
709 2018 : rv = nextValLiteral->GetValueConst(&s);
710 2018 : if (NS_FAILED(rv)) return rv;
711 :
712 2018 : PRInt32 nextVal = 0;
713 : {
714 4036 : for (const PRUnichar* p = s; *p != 0; ++p) {
715 2018 : NS_ASSERTION(*p >= '0' && *p <= '9', "not a digit");
716 2018 : if (*p < '0' || *p > '9')
717 0 : break;
718 :
719 2018 : nextVal *= 10;
720 2018 : nextVal += *p - '0';
721 : }
722 : }
723 :
724 : char buf[sizeof(kRDFNameSpaceURI) + 16];
725 4036 : nsFixedCString nextValStr(buf, sizeof(buf), 0);
726 2018 : nextValStr = kRDFNameSpaceURI;
727 2018 : nextValStr.Append("_");
728 2018 : nextValStr.AppendInt(nextVal, 10);
729 :
730 2018 : rv = gRDFService->GetResource(nextValStr, aResult);
731 2018 : if (NS_FAILED(rv)) return rv;
732 :
733 : // Now increment the RDF:nextVal property.
734 2018 : rv = mDataSource->Unassert(mContainer, kRDF_nextVal, nextValLiteral);
735 2018 : if (NS_FAILED(rv)) return rv;
736 :
737 2018 : ++nextVal;
738 2018 : nextValStr.Truncate();
739 2018 : nextValStr.AppendInt(nextVal, 10);
740 :
741 2018 : rv = gRDFService->GetLiteral(NS_ConvertASCIItoUTF16(nextValStr).get(), getter_AddRefs(nextValLiteral));
742 2018 : if (NS_FAILED(rv)) return rv;
743 :
744 2018 : rv = mDataSource->Assert(mContainer, kRDF_nextVal, nextValLiteral, true);
745 2018 : if (NS_FAILED(rv)) return rv;
746 :
747 2018 : if (RDF_SEQ_LIST_LIMIT == nextVal)
748 : {
749 : // focal point for RDF container mutation;
750 : // basically, provide a hint to allow for fast access
751 8 : nsCOMPtr<nsIRDFInMemoryDataSource> inMem = do_QueryInterface(mDataSource);
752 4 : if (inMem)
753 : {
754 : // ignore error; failure just means slower access
755 0 : (void)inMem->EnsureFastContainment(mContainer);
756 : }
757 : }
758 :
759 2018 : return NS_OK;
760 : }
|