michael@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* michael@0: michael@0: Implementation for the RDF container utils. michael@0: michael@0: */ michael@0: michael@0: michael@0: #include "nsCOMPtr.h" michael@0: #include "nsIServiceManager.h" michael@0: #include "nsIRDFContainer.h" michael@0: #include "nsIRDFContainerUtils.h" michael@0: #include "nsIRDFService.h" michael@0: #include "nsRDFCID.h" michael@0: #include "nsString.h" michael@0: #include "nsXPIDLString.h" michael@0: #include "plstr.h" michael@0: #include "prprf.h" michael@0: #include "rdf.h" michael@0: #include "rdfutil.h" michael@0: michael@0: class RDFContainerUtilsImpl : public nsIRDFContainerUtils michael@0: { michael@0: public: michael@0: // nsISupports interface michael@0: NS_DECL_ISUPPORTS michael@0: michael@0: // nsIRDFContainerUtils interface michael@0: NS_DECL_NSIRDFCONTAINERUTILS michael@0: michael@0: private: michael@0: friend nsresult NS_NewRDFContainerUtils(nsIRDFContainerUtils** aResult); michael@0: michael@0: RDFContainerUtilsImpl(); michael@0: virtual ~RDFContainerUtilsImpl(); michael@0: michael@0: nsresult MakeContainer(nsIRDFDataSource* aDataSource, michael@0: nsIRDFResource* aResource, michael@0: nsIRDFResource* aType, michael@0: nsIRDFContainer** aResult); michael@0: michael@0: bool IsA(nsIRDFDataSource* aDataSource, nsIRDFResource* aResource, nsIRDFResource* aType); michael@0: michael@0: // pseudo constants michael@0: static int32_t gRefCnt; michael@0: static nsIRDFService* gRDFService; michael@0: static nsIRDFResource* kRDF_instanceOf; michael@0: static nsIRDFResource* kRDF_nextVal; michael@0: static nsIRDFResource* kRDF_Bag; michael@0: static nsIRDFResource* kRDF_Seq; michael@0: static nsIRDFResource* kRDF_Alt; michael@0: static nsIRDFLiteral* kOne; michael@0: static const char kRDFNameSpaceURI[]; michael@0: }; michael@0: michael@0: int32_t RDFContainerUtilsImpl::gRefCnt = 0; michael@0: nsIRDFService* RDFContainerUtilsImpl::gRDFService; michael@0: nsIRDFResource* RDFContainerUtilsImpl::kRDF_instanceOf; michael@0: nsIRDFResource* RDFContainerUtilsImpl::kRDF_nextVal; michael@0: nsIRDFResource* RDFContainerUtilsImpl::kRDF_Bag; michael@0: nsIRDFResource* RDFContainerUtilsImpl::kRDF_Seq; michael@0: nsIRDFResource* RDFContainerUtilsImpl::kRDF_Alt; michael@0: nsIRDFLiteral* RDFContainerUtilsImpl::kOne; michael@0: const char RDFContainerUtilsImpl::kRDFNameSpaceURI[] = RDF_NAMESPACE_URI; michael@0: michael@0: //////////////////////////////////////////////////////////////////////// michael@0: // nsISupports interface michael@0: michael@0: NS_IMPL_ISUPPORTS(RDFContainerUtilsImpl, nsIRDFContainerUtils) michael@0: michael@0: //////////////////////////////////////////////////////////////////////// michael@0: // nsIRDFContainerUtils interface michael@0: michael@0: NS_IMETHODIMP michael@0: RDFContainerUtilsImpl::IsOrdinalProperty(nsIRDFResource *aProperty, bool *_retval) michael@0: { michael@0: NS_PRECONDITION(aProperty != nullptr, "null ptr"); michael@0: if (! aProperty) michael@0: return NS_ERROR_NULL_POINTER; michael@0: michael@0: nsresult rv; michael@0: michael@0: const char *propertyStr; michael@0: rv = aProperty->GetValueConst( &propertyStr ); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: if (PL_strncmp(propertyStr, kRDFNameSpaceURI, sizeof(kRDFNameSpaceURI) - 1) != 0) { michael@0: *_retval = false; michael@0: return NS_OK; michael@0: } michael@0: michael@0: const char* s = propertyStr; michael@0: s += sizeof(kRDFNameSpaceURI) - 1; michael@0: if (*s != '_') { michael@0: *_retval = false; michael@0: return NS_OK; michael@0: } michael@0: michael@0: ++s; michael@0: while (*s) { michael@0: if (*s < '0' || *s > '9') { michael@0: *_retval = false; michael@0: return NS_OK; michael@0: } michael@0: michael@0: ++s; michael@0: } michael@0: michael@0: *_retval = true; michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: RDFContainerUtilsImpl::IndexToOrdinalResource(int32_t aIndex, nsIRDFResource **aOrdinal) michael@0: { michael@0: NS_PRECONDITION(aIndex > 0, "illegal value"); michael@0: if (aIndex <= 0) michael@0: return NS_ERROR_ILLEGAL_VALUE; michael@0: michael@0: nsAutoCString uri(kRDFNameSpaceURI); michael@0: uri.Append('_'); michael@0: uri.AppendInt(aIndex); michael@0: michael@0: nsresult rv = gRDFService->GetResource(uri, aOrdinal); michael@0: NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get ordinal resource"); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: RDFContainerUtilsImpl::OrdinalResourceToIndex(nsIRDFResource *aOrdinal, int32_t *aIndex) michael@0: { michael@0: NS_PRECONDITION(aOrdinal != nullptr, "null ptr"); michael@0: if (! aOrdinal) michael@0: return NS_ERROR_NULL_POINTER; michael@0: michael@0: const char *ordinalStr; michael@0: if (NS_FAILED(aOrdinal->GetValueConst( &ordinalStr ))) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: const char* s = ordinalStr; michael@0: if (PL_strncmp(s, kRDFNameSpaceURI, sizeof(kRDFNameSpaceURI) - 1) != 0) { michael@0: NS_ERROR("not an ordinal"); michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: s += sizeof(kRDFNameSpaceURI) - 1; michael@0: if (*s != '_') { michael@0: NS_ERROR("not an ordinal"); michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: int32_t idx = 0; michael@0: michael@0: ++s; michael@0: while (*s) { michael@0: if (*s < '0' || *s > '9') { michael@0: NS_ERROR("not an ordinal"); michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: idx *= 10; michael@0: idx += (*s - '0'); michael@0: michael@0: ++s; michael@0: } michael@0: michael@0: *aIndex = idx; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: RDFContainerUtilsImpl::IsContainer(nsIRDFDataSource *aDataSource, nsIRDFResource *aResource, bool *_retval) michael@0: { michael@0: NS_PRECONDITION(aDataSource != nullptr, "null ptr"); michael@0: if (! aDataSource) michael@0: return NS_ERROR_NULL_POINTER; michael@0: michael@0: NS_PRECONDITION(aResource != nullptr, "null ptr"); michael@0: if (! aResource) michael@0: return NS_ERROR_NULL_POINTER; michael@0: michael@0: NS_PRECONDITION(_retval != nullptr, "null ptr"); michael@0: if (! _retval) michael@0: return NS_ERROR_NULL_POINTER; michael@0: michael@0: if (IsA(aDataSource, aResource, kRDF_Seq) || michael@0: IsA(aDataSource, aResource, kRDF_Bag) || michael@0: IsA(aDataSource, aResource, kRDF_Alt)) { michael@0: *_retval = true; michael@0: } michael@0: else { michael@0: *_retval = false; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: RDFContainerUtilsImpl::IsEmpty(nsIRDFDataSource* aDataSource, nsIRDFResource* aResource, bool* _retval) michael@0: { michael@0: if (! aDataSource) michael@0: return NS_ERROR_NULL_POINTER; michael@0: michael@0: nsresult rv; michael@0: michael@0: // By default, say that we're an empty container. Even if we're not michael@0: // really even a container. michael@0: *_retval = true; michael@0: michael@0: nsCOMPtr nextValNode; michael@0: rv = aDataSource->GetTarget(aResource, kRDF_nextVal, true, getter_AddRefs(nextValNode)); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: if (rv == NS_RDF_NO_VALUE) michael@0: return NS_OK; michael@0: michael@0: nsCOMPtr nextValLiteral; michael@0: rv = nextValNode->QueryInterface(NS_GET_IID(nsIRDFLiteral), getter_AddRefs(nextValLiteral)); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: if (nextValLiteral.get() != kOne) michael@0: *_retval = false; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: RDFContainerUtilsImpl::IsBag(nsIRDFDataSource *aDataSource, nsIRDFResource *aResource, bool *_retval) michael@0: { michael@0: NS_PRECONDITION(aDataSource != nullptr, "null ptr"); michael@0: if (! aDataSource) michael@0: return NS_ERROR_NULL_POINTER; michael@0: michael@0: NS_PRECONDITION(aResource != nullptr, "null ptr"); michael@0: if (! aResource) michael@0: return NS_ERROR_NULL_POINTER; michael@0: michael@0: NS_PRECONDITION(_retval != nullptr, "null ptr"); michael@0: if (! _retval) michael@0: return NS_ERROR_NULL_POINTER; michael@0: michael@0: *_retval = IsA(aDataSource, aResource, kRDF_Bag); michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: RDFContainerUtilsImpl::IsSeq(nsIRDFDataSource *aDataSource, nsIRDFResource *aResource, bool *_retval) michael@0: { michael@0: NS_PRECONDITION(aDataSource != nullptr, "null ptr"); michael@0: if (! aDataSource) michael@0: return NS_ERROR_NULL_POINTER; michael@0: michael@0: NS_PRECONDITION(aResource != nullptr, "null ptr"); michael@0: if (! aResource) michael@0: return NS_ERROR_NULL_POINTER; michael@0: michael@0: NS_PRECONDITION(_retval != nullptr, "null ptr"); michael@0: if (! _retval) michael@0: return NS_ERROR_NULL_POINTER; michael@0: michael@0: *_retval = IsA(aDataSource, aResource, kRDF_Seq); michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: RDFContainerUtilsImpl::IsAlt(nsIRDFDataSource *aDataSource, nsIRDFResource *aResource, bool *_retval) michael@0: { michael@0: NS_PRECONDITION(aDataSource != nullptr, "null ptr"); michael@0: if (! aDataSource) michael@0: return NS_ERROR_NULL_POINTER; michael@0: michael@0: NS_PRECONDITION(aResource != nullptr, "null ptr"); michael@0: if (! aResource) michael@0: return NS_ERROR_NULL_POINTER; michael@0: michael@0: NS_PRECONDITION(_retval != nullptr, "null ptr"); michael@0: if (! _retval) michael@0: return NS_ERROR_NULL_POINTER; michael@0: michael@0: *_retval = IsA(aDataSource, aResource, kRDF_Alt); michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: RDFContainerUtilsImpl::MakeBag(nsIRDFDataSource *aDataSource, nsIRDFResource *aResource, nsIRDFContainer **_retval) michael@0: { michael@0: return MakeContainer(aDataSource, aResource, kRDF_Bag, _retval); michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: RDFContainerUtilsImpl::MakeSeq(nsIRDFDataSource *aDataSource, nsIRDFResource *aResource, nsIRDFContainer **_retval) michael@0: { michael@0: return MakeContainer(aDataSource, aResource, kRDF_Seq, _retval); michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: RDFContainerUtilsImpl::MakeAlt(nsIRDFDataSource *aDataSource, nsIRDFResource *aResource, nsIRDFContainer **_retval) michael@0: { michael@0: return MakeContainer(aDataSource, aResource, kRDF_Alt, _retval); michael@0: } michael@0: michael@0: michael@0: michael@0: //////////////////////////////////////////////////////////////////////// michael@0: michael@0: michael@0: RDFContainerUtilsImpl::RDFContainerUtilsImpl() michael@0: { michael@0: if (gRefCnt++ == 0) { michael@0: nsresult rv; michael@0: michael@0: NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID); michael@0: rv = CallGetService(kRDFServiceCID, &gRDFService); michael@0: michael@0: NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get RDF service"); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: gRDFService->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "instanceOf"), michael@0: &kRDF_instanceOf); michael@0: gRDFService->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "nextVal"), michael@0: &kRDF_nextVal); michael@0: gRDFService->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "Bag"), michael@0: &kRDF_Bag); michael@0: gRDFService->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "Seq"), michael@0: &kRDF_Seq); michael@0: gRDFService->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "Alt"), michael@0: &kRDF_Alt); michael@0: gRDFService->GetLiteral(MOZ_UTF16("1"), &kOne); michael@0: } michael@0: } michael@0: } michael@0: michael@0: michael@0: RDFContainerUtilsImpl::~RDFContainerUtilsImpl() michael@0: { michael@0: #ifdef DEBUG_REFS michael@0: --gInstanceCount; michael@0: fprintf(stdout, "%d - RDF: RDFContainerUtilsImpl\n", gInstanceCount); michael@0: #endif michael@0: michael@0: if (--gRefCnt == 0) { michael@0: NS_IF_RELEASE(gRDFService); michael@0: NS_IF_RELEASE(kRDF_instanceOf); michael@0: NS_IF_RELEASE(kRDF_nextVal); michael@0: NS_IF_RELEASE(kRDF_Bag); michael@0: NS_IF_RELEASE(kRDF_Seq); michael@0: NS_IF_RELEASE(kRDF_Alt); michael@0: NS_IF_RELEASE(kOne); michael@0: } michael@0: } michael@0: michael@0: michael@0: michael@0: nsresult michael@0: NS_NewRDFContainerUtils(nsIRDFContainerUtils** aResult) michael@0: { michael@0: NS_PRECONDITION(aResult != nullptr, "null ptr"); michael@0: if (! aResult) michael@0: return NS_ERROR_NULL_POINTER; michael@0: michael@0: RDFContainerUtilsImpl* result = michael@0: new RDFContainerUtilsImpl(); michael@0: michael@0: if (! result) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: NS_ADDREF(result); michael@0: *aResult = result; michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: nsresult michael@0: RDFContainerUtilsImpl::MakeContainer(nsIRDFDataSource* aDataSource, nsIRDFResource* aResource, nsIRDFResource* aType, nsIRDFContainer** aResult) michael@0: { michael@0: NS_PRECONDITION(aDataSource != nullptr, "null ptr"); michael@0: if (! aDataSource) return NS_ERROR_NULL_POINTER; michael@0: michael@0: NS_PRECONDITION(aResource != nullptr, "null ptr"); michael@0: if (! aResource) return NS_ERROR_NULL_POINTER; michael@0: michael@0: NS_PRECONDITION(aType != nullptr, "null ptr"); michael@0: if (! aType) return NS_ERROR_NULL_POINTER; michael@0: michael@0: if (aResult) *aResult = nullptr; michael@0: michael@0: nsresult rv; michael@0: michael@0: // Check to see if somebody has already turned it into a container; if so michael@0: // don't try to do it again. michael@0: bool isContainer; michael@0: rv = IsContainer(aDataSource, aResource, &isContainer); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: if (!isContainer) michael@0: { michael@0: rv = aDataSource->Assert(aResource, kRDF_instanceOf, aType, true); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = aDataSource->Assert(aResource, kRDF_nextVal, kOne, true); michael@0: if (NS_FAILED(rv)) return rv; michael@0: } michael@0: michael@0: if (aResult) { michael@0: rv = NS_NewRDFContainer(aResult); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = (*aResult)->Init(aDataSource, aResource); michael@0: if (NS_FAILED(rv)) return rv; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: bool michael@0: RDFContainerUtilsImpl::IsA(nsIRDFDataSource* aDataSource, nsIRDFResource* aResource, nsIRDFResource* aType) michael@0: { michael@0: if (!aDataSource || !aResource || !aType) { michael@0: NS_WARNING("Unexpected null argument"); michael@0: return false; michael@0: } michael@0: michael@0: nsresult rv; michael@0: michael@0: bool result; michael@0: rv = aDataSource->HasAssertion(aResource, kRDF_instanceOf, aType, true, &result); michael@0: if (NS_FAILED(rv)) michael@0: return false; michael@0: michael@0: return result; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: RDFContainerUtilsImpl::IndexOf(nsIRDFDataSource* aDataSource, nsIRDFResource* aContainer, nsIRDFNode* aElement, int32_t* aIndex) michael@0: { michael@0: if (!aDataSource || !aContainer) michael@0: return NS_ERROR_NULL_POINTER; michael@0: michael@0: // Assume we can't find it. michael@0: *aIndex = -1; michael@0: michael@0: // If the resource is null, bail quietly michael@0: if (! aElement) michael@0: return NS_OK; michael@0: michael@0: // We'll assume that fan-out is much higher than fan-in, so grovel michael@0: // through the inbound arcs, look for an ordinal resource, and michael@0: // decode it. michael@0: nsCOMPtr arcsIn; michael@0: aDataSource->ArcLabelsIn(aElement, getter_AddRefs(arcsIn)); michael@0: if (! arcsIn) michael@0: return NS_OK; michael@0: michael@0: while (1) { michael@0: bool hasMoreArcs = false; michael@0: arcsIn->HasMoreElements(&hasMoreArcs); michael@0: if (! hasMoreArcs) michael@0: break; michael@0: michael@0: nsCOMPtr isupports; michael@0: arcsIn->GetNext(getter_AddRefs(isupports)); michael@0: if (! isupports) michael@0: break; michael@0: michael@0: nsCOMPtr property = michael@0: do_QueryInterface(isupports); michael@0: michael@0: if (! property) michael@0: continue; michael@0: michael@0: bool isOrdinal; michael@0: IsOrdinalProperty(property, &isOrdinal); michael@0: if (! isOrdinal) michael@0: continue; michael@0: michael@0: nsCOMPtr sources; michael@0: aDataSource->GetSources(property, aElement, true, getter_AddRefs(sources)); michael@0: if (! sources) michael@0: continue; michael@0: michael@0: while (1) { michael@0: bool hasMoreSources = false; michael@0: sources->HasMoreElements(&hasMoreSources); michael@0: if (! hasMoreSources) michael@0: break; michael@0: michael@0: nsCOMPtr isupports2; michael@0: sources->GetNext(getter_AddRefs(isupports2)); michael@0: if (! isupports2) michael@0: break; michael@0: michael@0: nsCOMPtr source = michael@0: do_QueryInterface(isupports2); michael@0: michael@0: if (source == aContainer) michael@0: // Found it. michael@0: return OrdinalResourceToIndex(property, aIndex); michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: }