nsContainerEnumerator.cpp 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  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/. */
  5. /*
  6. A simple cursor that enumerates the elements of an RDF container
  7. (RDF:Bag, RDF:Seq, or RDF:Alt).
  8. Caveats
  9. -------
  10. 1. This uses an implementation-specific detail to determine the
  11. index of the last element in the container; specifically, the RDF
  12. utilities maintain a counter attribute on the container that
  13. holds the numeric value of the next value that is to be
  14. assigned. So, this cursor will bust if you use it with a bag that
  15. hasn't been created using the RDF utility routines.
  16. */
  17. #include "nscore.h"
  18. #include "nsCOMPtr.h"
  19. #include "nsIRDFContainerUtils.h"
  20. #include "nsIRDFDataSource.h"
  21. #include "nsIRDFNode.h"
  22. #include "nsIRDFService.h"
  23. #include "nsIServiceManager.h"
  24. #include "nsRDFCID.h"
  25. #include "nsString.h"
  26. #include "nsXPIDLString.h"
  27. #include "mozilla/Logging.h"
  28. #include "rdf.h"
  29. #include "rdfutil.h"
  30. ////////////////////////////////////////////////////////////////////////
  31. class ContainerEnumeratorImpl : public nsISimpleEnumerator {
  32. private:
  33. // pseudo-constants
  34. static nsrefcnt gRefCnt;
  35. static nsIRDFResource* kRDF_nextVal;
  36. static nsIRDFContainerUtils* gRDFC;
  37. nsCOMPtr<nsIRDFDataSource> mDataSource;
  38. nsCOMPtr<nsIRDFResource> mContainer;
  39. nsCOMPtr<nsIRDFResource> mOrdinalProperty;
  40. nsCOMPtr<nsISimpleEnumerator> mCurrent;
  41. nsCOMPtr<nsIRDFNode> mResult;
  42. int32_t mNextIndex;
  43. virtual ~ContainerEnumeratorImpl();
  44. public:
  45. ContainerEnumeratorImpl(nsIRDFDataSource* ds, nsIRDFResource* container);
  46. nsresult Init();
  47. NS_DECL_ISUPPORTS
  48. NS_DECL_NSISIMPLEENUMERATOR
  49. };
  50. nsrefcnt ContainerEnumeratorImpl::gRefCnt;
  51. nsIRDFResource* ContainerEnumeratorImpl::kRDF_nextVal;
  52. nsIRDFContainerUtils* ContainerEnumeratorImpl::gRDFC;
  53. ContainerEnumeratorImpl::ContainerEnumeratorImpl(nsIRDFDataSource* aDataSource,
  54. nsIRDFResource* aContainer)
  55. : mDataSource(aDataSource),
  56. mContainer(aContainer),
  57. mNextIndex(1)
  58. {
  59. }
  60. nsresult
  61. ContainerEnumeratorImpl::Init()
  62. {
  63. if (gRefCnt++ == 0) {
  64. nsresult rv;
  65. NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID);
  66. nsCOMPtr<nsIRDFService> rdf = do_GetService(kRDFServiceCID);
  67. NS_ASSERTION(rdf != nullptr, "unable to acquire resource manager");
  68. if (! rdf)
  69. return NS_ERROR_FAILURE;
  70. rv = rdf->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "nextVal"), &kRDF_nextVal);
  71. NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get resource");
  72. if (NS_FAILED(rv)) return rv;
  73. NS_DEFINE_CID(kRDFContainerUtilsCID, NS_RDFCONTAINERUTILS_CID);
  74. rv = CallGetService(kRDFContainerUtilsCID, &gRDFC);
  75. if (NS_FAILED(rv)) return rv;
  76. }
  77. return NS_OK;
  78. }
  79. ContainerEnumeratorImpl::~ContainerEnumeratorImpl()
  80. {
  81. if (--gRefCnt == 0) {
  82. NS_IF_RELEASE(kRDF_nextVal);
  83. NS_IF_RELEASE(gRDFC);
  84. }
  85. }
  86. NS_IMPL_ISUPPORTS(ContainerEnumeratorImpl, nsISimpleEnumerator)
  87. NS_IMETHODIMP
  88. ContainerEnumeratorImpl::HasMoreElements(bool* aResult)
  89. {
  90. NS_PRECONDITION(aResult != nullptr, "null ptr");
  91. if (! aResult)
  92. return NS_ERROR_NULL_POINTER;
  93. nsresult rv;
  94. // If we've already queued up a next value, then we know there are more elements.
  95. if (mResult) {
  96. *aResult = true;
  97. return NS_OK;
  98. }
  99. // Otherwise, we need to grovel
  100. // Figure out the upper bound so we'll know when we're done. Since it's
  101. // possible that we're targeting a composite datasource, we'll need to
  102. // "GetTargets()" and take the maximum value of "nextVal" to know the
  103. // upper bound.
  104. //
  105. // Remember that since nextVal is the next index that we'd assign
  106. // to an element in a container, it's *one more* than count of
  107. // elements in the container.
  108. int32_t max = 0;
  109. nsCOMPtr<nsISimpleEnumerator> targets;
  110. rv = mDataSource->GetTargets(mContainer, kRDF_nextVal, true, getter_AddRefs(targets));
  111. if (NS_FAILED(rv)) return rv;
  112. while (1) {
  113. bool hasmore;
  114. targets->HasMoreElements(&hasmore);
  115. if (! hasmore)
  116. break;
  117. nsCOMPtr<nsISupports> isupports;
  118. targets->GetNext(getter_AddRefs(isupports));
  119. nsCOMPtr<nsIRDFLiteral> nextValLiteral = do_QueryInterface(isupports);
  120. if (! nextValLiteral)
  121. continue;
  122. const char16_t *nextValStr;
  123. nextValLiteral->GetValueConst(&nextValStr);
  124. nsresult err;
  125. int32_t nextVal = nsAutoString(nextValStr).ToInteger(&err);
  126. if (nextVal > max)
  127. max = nextVal;
  128. }
  129. // Now pre-fetch our next value into mResult.
  130. while (mCurrent || mNextIndex < max) {
  131. // If mCurrent has been depleted, then conjure up a new one
  132. if (! mCurrent) {
  133. rv = gRDFC->IndexToOrdinalResource(mNextIndex, getter_AddRefs(mOrdinalProperty));
  134. if (NS_FAILED(rv)) return rv;
  135. rv = mDataSource->GetTargets(mContainer, mOrdinalProperty, true, getter_AddRefs(mCurrent));
  136. if (NS_FAILED(rv)) return rv;
  137. ++mNextIndex;
  138. }
  139. if (mCurrent) {
  140. bool hasMore;
  141. rv = mCurrent->HasMoreElements(&hasMore);
  142. if (NS_FAILED(rv)) return rv;
  143. // Is the current enumerator depleted? If so, iterate to
  144. // the next index.
  145. if (! hasMore) {
  146. mCurrent = nullptr;
  147. continue;
  148. }
  149. // "Peek" ahead and pull out the next target.
  150. nsCOMPtr<nsISupports> result;
  151. rv = mCurrent->GetNext(getter_AddRefs(result));
  152. if (NS_FAILED(rv)) return rv;
  153. mResult = do_QueryInterface(result, &rv);
  154. if (NS_FAILED(rv)) return rv;
  155. *aResult = true;
  156. return NS_OK;
  157. }
  158. }
  159. // If we get here, we ran out of elements. The cursor is empty.
  160. *aResult = false;
  161. return NS_OK;
  162. }
  163. NS_IMETHODIMP
  164. ContainerEnumeratorImpl::GetNext(nsISupports** aResult)
  165. {
  166. nsresult rv;
  167. bool hasMore;
  168. rv = HasMoreElements(&hasMore);
  169. if (NS_FAILED(rv)) return rv;
  170. if (! hasMore)
  171. return NS_ERROR_UNEXPECTED;
  172. NS_ADDREF(*aResult = mResult);
  173. mResult = nullptr;
  174. return NS_OK;
  175. }
  176. ////////////////////////////////////////////////////////////////////////
  177. nsresult
  178. NS_NewContainerEnumerator(nsIRDFDataSource* aDataSource,
  179. nsIRDFResource* aContainer,
  180. nsISimpleEnumerator** aResult)
  181. {
  182. NS_PRECONDITION(aDataSource != nullptr, "null ptr");
  183. if (! aDataSource)
  184. return NS_ERROR_NULL_POINTER;
  185. NS_PRECONDITION(aContainer != nullptr, "null ptr");
  186. if (! aContainer)
  187. return NS_ERROR_NULL_POINTER;
  188. NS_PRECONDITION(aResult != nullptr, "null ptr");
  189. if (! aResult)
  190. return NS_ERROR_NULL_POINTER;
  191. ContainerEnumeratorImpl* result = new ContainerEnumeratorImpl(aDataSource, aContainer);
  192. if (! result)
  193. return NS_ERROR_OUT_OF_MEMORY;
  194. NS_ADDREF(result);
  195. nsresult rv = result->Init();
  196. if (NS_FAILED(rv))
  197. NS_RELEASE(result);
  198. *aResult = result;
  199. return rv;
  200. }