gecko/rdf/base/nsRDFService.cpp

1566 lines
41 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*
* This Original Code has been modified by IBM Corporation.
* Modifications made by IBM described herein are
* Copyright (c) International Business Machines
* Corporation, 2000
*
* Modifications to Mozilla code or documentation
* identified per MPL Section 3.3
*
* Date Modified by Description of modification
* 03/27/2000 IBM Corp. Added PR_CALLBACK for Optlink
* use in OS2
*/
/*
This file provides the implementation for the RDF service manager.
TO DO
-----
1) Implement the CreateDataBase() methods.
2) Cache date and int literals.
*/
#include "nsRDFService.h"
#include "nsCOMPtr.h"
#include "nsAutoPtr.h"
#include "nsMemory.h"
#include "nsIAtom.h"
#include "nsIComponentManager.h"
#include "nsIRDFDataSource.h"
#include "nsIRDFNode.h"
#include "nsIRDFRemoteDataSource.h"
#include "nsIServiceManager.h"
#include "nsIFactory.h"
#include "nsRDFCID.h"
#include "nsString.h"
#include "nsXPIDLString.h"
#include "nsNetUtil.h"
#include "nsIURI.h"
#include "pldhash.h"
#include "plhash.h"
#include "plstr.h"
#include "mozilla/Logging.h"
#include "prprf.h"
#include "prmem.h"
#include "rdf.h"
#include "nsCRT.h"
#include "nsCRTGlue.h"
#include "mozilla/HashFunctions.h"
using namespace mozilla;
////////////////////////////////////////////////////////////////////////
static NS_DEFINE_CID(kRDFXMLDataSourceCID, NS_RDFXMLDATASOURCE_CID);
static NS_DEFINE_CID(kRDFDefaultResourceCID, NS_RDFDEFAULTRESOURCE_CID);
static NS_DEFINE_IID(kIRDFLiteralIID, NS_IRDFLITERAL_IID);
static NS_DEFINE_IID(kIRDFDateIID, NS_IRDFDATE_IID);
static NS_DEFINE_IID(kIRDFIntIID, NS_IRDFINT_IID);
static NS_DEFINE_IID(kIRDFNodeIID, NS_IRDFNODE_IID);
static NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID);
static PRLogModuleInfo* gLog = nullptr;
class BlobImpl;
// These functions are copied from nsprpub/lib/ds/plhash.c, with one
// change to free the key in DataSourceFreeEntry.
// XXX sigh, why were DefaultAllocTable et. al. declared static, anyway?
static void *
DataSourceAllocTable(void *pool, size_t size)
{
return PR_MALLOC(size);
}
static void
DataSourceFreeTable(void *pool, void *item)
{
PR_Free(item);
}
static PLHashEntry *
DataSourceAllocEntry(void *pool, const void *key)
{
return PR_NEW(PLHashEntry);
}
static void
DataSourceFreeEntry(void *pool, PLHashEntry *he, unsigned flag)
{
if (flag == HT_FREE_ENTRY) {
PL_strfree((char*) he->key);
PR_Free(he);
}
}
static PLHashAllocOps dataSourceHashAllocOps = {
DataSourceAllocTable, DataSourceFreeTable,
DataSourceAllocEntry, DataSourceFreeEntry
};
//----------------------------------------------------------------------
//
// For the mResources hashtable.
//
struct ResourceHashEntry : public PLDHashEntryHdr {
const char *mKey;
nsIRDFResource *mResource;
static PLDHashNumber
HashKey(PLDHashTable *table, const void *key)
{
return HashString(static_cast<const char *>(key));
}
static bool
MatchEntry(PLDHashTable *table, const PLDHashEntryHdr *hdr,
const void *key)
{
const ResourceHashEntry *entry =
static_cast<const ResourceHashEntry *>(hdr);
return 0 == nsCRT::strcmp(static_cast<const char *>(key),
entry->mKey);
}
};
static const PLDHashTableOps gResourceTableOps = {
ResourceHashEntry::HashKey,
ResourceHashEntry::MatchEntry,
PL_DHashMoveEntryStub,
PL_DHashClearEntryStub,
nullptr
};
// ----------------------------------------------------------------------
//
// For the mLiterals hashtable.
//
struct LiteralHashEntry : public PLDHashEntryHdr {
nsIRDFLiteral *mLiteral;
const char16_t *mKey;
static PLDHashNumber
HashKey(PLDHashTable *table, const void *key)
{
return HashString(static_cast<const char16_t *>(key));
}
static bool
MatchEntry(PLDHashTable *table, const PLDHashEntryHdr *hdr,
const void *key)
{
const LiteralHashEntry *entry =
static_cast<const LiteralHashEntry *>(hdr);
return 0 == nsCRT::strcmp(static_cast<const char16_t *>(key),
entry->mKey);
}
};
static const PLDHashTableOps gLiteralTableOps = {
LiteralHashEntry::HashKey,
LiteralHashEntry::MatchEntry,
PL_DHashMoveEntryStub,
PL_DHashClearEntryStub,
nullptr
};
// ----------------------------------------------------------------------
//
// For the mInts hashtable.
//
struct IntHashEntry : public PLDHashEntryHdr {
nsIRDFInt *mInt;
int32_t mKey;
static PLDHashNumber
HashKey(PLDHashTable *table, const void *key)
{
return PLDHashNumber(*static_cast<const int32_t *>(key));
}
static bool
MatchEntry(PLDHashTable *table, const PLDHashEntryHdr *hdr,
const void *key)
{
const IntHashEntry *entry =
static_cast<const IntHashEntry *>(hdr);
return *static_cast<const int32_t *>(key) == entry->mKey;
}
};
static const PLDHashTableOps gIntTableOps = {
IntHashEntry::HashKey,
IntHashEntry::MatchEntry,
PL_DHashMoveEntryStub,
PL_DHashClearEntryStub,
nullptr
};
// ----------------------------------------------------------------------
//
// For the mDates hashtable.
//
struct DateHashEntry : public PLDHashEntryHdr {
nsIRDFDate *mDate;
PRTime mKey;
static PLDHashNumber
HashKey(PLDHashTable *table, const void *key)
{
// xor the low 32 bits with the high 32 bits.
PRTime t = *static_cast<const PRTime *>(key);
int32_t h32 = int32_t(t >> 32);
int32_t l32 = int32_t(0xffffffff & t);
return PLDHashNumber(l32 ^ h32);
}
static bool
MatchEntry(PLDHashTable *table, const PLDHashEntryHdr *hdr,
const void *key)
{
const DateHashEntry *entry =
static_cast<const DateHashEntry *>(hdr);
return *static_cast<const PRTime *>(key) == entry->mKey;
}
};
static const PLDHashTableOps gDateTableOps = {
DateHashEntry::HashKey,
DateHashEntry::MatchEntry,
PL_DHashMoveEntryStub,
PL_DHashClearEntryStub,
nullptr
};
class BlobImpl : public nsIRDFBlob
{
public:
struct Data {
int32_t mLength;
uint8_t *mBytes;
};
BlobImpl(const uint8_t *aBytes, int32_t aLength)
{
mData.mLength = aLength;
mData.mBytes = new uint8_t[aLength];
memcpy(mData.mBytes, aBytes, aLength);
NS_ADDREF(RDFServiceImpl::gRDFService);
RDFServiceImpl::gRDFService->RegisterBlob(this);
}
protected:
virtual ~BlobImpl()
{
RDFServiceImpl::gRDFService->UnregisterBlob(this);
// Use NS_RELEASE2() here, because we want to decrease the
// refcount, but not null out the gRDFService pointer (which is
// what a vanilla NS_RELEASE() would do).
nsrefcnt refcnt;
NS_RELEASE2(RDFServiceImpl::gRDFService, refcnt);
delete[] mData.mBytes;
}
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIRDFNODE
NS_DECL_NSIRDFBLOB
Data mData;
};
NS_IMPL_ISUPPORTS(BlobImpl, nsIRDFNode, nsIRDFBlob)
NS_IMETHODIMP
BlobImpl::EqualsNode(nsIRDFNode *aNode, bool *aEquals)
{
nsCOMPtr<nsIRDFBlob> blob = do_QueryInterface(aNode);
if (blob) {
int32_t length;
blob->GetLength(&length);
if (length == mData.mLength) {
const uint8_t *bytes;
blob->GetValue(&bytes);
if (0 == memcmp(bytes, mData.mBytes, length)) {
*aEquals = true;
return NS_OK;
}
}
}
*aEquals = false;
return NS_OK;
}
NS_IMETHODIMP
BlobImpl::GetValue(const uint8_t **aResult)
{
*aResult = mData.mBytes;
return NS_OK;
}
NS_IMETHODIMP
BlobImpl::GetLength(int32_t *aResult)
{
*aResult = mData.mLength;
return NS_OK;
}
// ----------------------------------------------------------------------
//
// For the mBlobs hashtable.
//
struct BlobHashEntry : public PLDHashEntryHdr {
BlobImpl *mBlob;
static PLDHashNumber
HashKey(PLDHashTable *table, const void *key)
{
const BlobImpl::Data *data =
static_cast<const BlobImpl::Data *>(key);
return HashBytes(data->mBytes, data->mLength);
}
static bool
MatchEntry(PLDHashTable *table, const PLDHashEntryHdr *hdr,
const void *key)
{
const BlobHashEntry *entry =
static_cast<const BlobHashEntry *>(hdr);
const BlobImpl::Data *left = &entry->mBlob->mData;
const BlobImpl::Data *right =
static_cast<const BlobImpl::Data *>(key);
return (left->mLength == right->mLength)
&& 0 == memcmp(left->mBytes, right->mBytes, right->mLength);
}
};
static const PLDHashTableOps gBlobTableOps = {
BlobHashEntry::HashKey,
BlobHashEntry::MatchEntry,
PL_DHashMoveEntryStub,
PL_DHashClearEntryStub,
nullptr
};
////////////////////////////////////////////////////////////////////////
// LiteralImpl
//
// Currently, all literals are implemented exactly the same way;
// i.e., there is are no resource factories to allow you to generate
// customer resources. I doubt that makes sense, anyway.
//
class LiteralImpl : public nsIRDFLiteral {
public:
static nsresult
Create(const char16_t* aValue, nsIRDFLiteral** aResult);
// nsISupports
NS_DECL_THREADSAFE_ISUPPORTS
// nsIRDFNode
NS_DECL_NSIRDFNODE
// nsIRDFLiteral
NS_DECL_NSIRDFLITERAL
protected:
explicit LiteralImpl(const char16_t* s);
virtual ~LiteralImpl();
const char16_t* GetValue() const {
size_t objectSize = ((sizeof(LiteralImpl) + sizeof(char16_t) - 1) / sizeof(char16_t)) * sizeof(char16_t);
return reinterpret_cast<const char16_t*>(reinterpret_cast<const unsigned char*>(this) + objectSize);
}
};
nsresult
LiteralImpl::Create(const char16_t* aValue, nsIRDFLiteral** aResult)
{
// Goofy math to get alignment right. Copied from nsSharedString.h.
size_t objectSize = ((sizeof(LiteralImpl) + sizeof(char16_t) - 1) / sizeof(char16_t)) * sizeof(char16_t);
size_t stringLen = nsCharTraits<char16_t>::length(aValue);
size_t stringSize = (stringLen + 1) * sizeof(char16_t);
void* objectPtr = operator new(objectSize + stringSize);
if (! objectPtr)
return NS_ERROR_NULL_POINTER;
char16_t* buf = reinterpret_cast<char16_t*>(static_cast<unsigned char*>(objectPtr) + objectSize);
nsCharTraits<char16_t>::copy(buf, aValue, stringLen + 1);
NS_ADDREF(*aResult = new (objectPtr) LiteralImpl(buf));
return NS_OK;
}
LiteralImpl::LiteralImpl(const char16_t* s)
{
RDFServiceImpl::gRDFService->RegisterLiteral(this);
NS_ADDREF(RDFServiceImpl::gRDFService);
}
LiteralImpl::~LiteralImpl()
{
RDFServiceImpl::gRDFService->UnregisterLiteral(this);
// Use NS_RELEASE2() here, because we want to decrease the
// refcount, but not null out the gRDFService pointer (which is
// what a vanilla NS_RELEASE() would do).
nsrefcnt refcnt;
NS_RELEASE2(RDFServiceImpl::gRDFService, refcnt);
}
NS_IMPL_ADDREF(LiteralImpl)
NS_IMPL_RELEASE(LiteralImpl)
nsresult
LiteralImpl::QueryInterface(REFNSIID iid, void** result)
{
if (! result)
return NS_ERROR_NULL_POINTER;
*result = nullptr;
if (iid.Equals(kIRDFLiteralIID) ||
iid.Equals(kIRDFNodeIID) ||
iid.Equals(kISupportsIID)) {
*result = static_cast<nsIRDFLiteral*>(this);
AddRef();
return NS_OK;
}
return NS_NOINTERFACE;
}
NS_IMETHODIMP
LiteralImpl::EqualsNode(nsIRDFNode* aNode, bool* aResult)
{
nsresult rv;
nsIRDFLiteral* literal;
rv = aNode->QueryInterface(kIRDFLiteralIID, (void**) &literal);
if (NS_SUCCEEDED(rv)) {
*aResult = (static_cast<nsIRDFLiteral*>(this) == literal);
NS_RELEASE(literal);
return NS_OK;
}
else if (rv == NS_NOINTERFACE) {
*aResult = false;
return NS_OK;
}
else {
return rv;
}
}
NS_IMETHODIMP
LiteralImpl::GetValue(char16_t* *value)
{
NS_ASSERTION(value, "null ptr");
if (! value)
return NS_ERROR_NULL_POINTER;
const char16_t *temp = GetValue();
*value = temp? NS_strdup(temp) : 0;
return NS_OK;
}
NS_IMETHODIMP
LiteralImpl::GetValueConst(const char16_t** aValue)
{
*aValue = GetValue();
return NS_OK;
}
////////////////////////////////////////////////////////////////////////
// DateImpl
//
class DateImpl : public nsIRDFDate {
public:
explicit DateImpl(const PRTime s);
// nsISupports
NS_DECL_ISUPPORTS
// nsIRDFNode
NS_DECL_NSIRDFNODE
// nsIRDFDate
NS_IMETHOD GetValue(PRTime *value) override;
private:
virtual ~DateImpl();
nsresult EqualsDate(nsIRDFDate* date, bool* result);
PRTime mValue;
};
DateImpl::DateImpl(const PRTime s)
: mValue(s)
{
RDFServiceImpl::gRDFService->RegisterDate(this);
NS_ADDREF(RDFServiceImpl::gRDFService);
}
DateImpl::~DateImpl()
{
RDFServiceImpl::gRDFService->UnregisterDate(this);
// Use NS_RELEASE2() here, because we want to decrease the
// refcount, but not null out the gRDFService pointer (which is
// what a vanilla NS_RELEASE() would do).
nsrefcnt refcnt;
NS_RELEASE2(RDFServiceImpl::gRDFService, refcnt);
}
NS_IMPL_ADDREF(DateImpl)
NS_IMPL_RELEASE(DateImpl)
nsresult
DateImpl::QueryInterface(REFNSIID iid, void** result)
{
if (! result)
return NS_ERROR_NULL_POINTER;
*result = nullptr;
if (iid.Equals(kIRDFDateIID) ||
iid.Equals(kIRDFNodeIID) ||
iid.Equals(kISupportsIID)) {
*result = static_cast<nsIRDFDate*>(this);
AddRef();
return NS_OK;
}
return NS_NOINTERFACE;
}
NS_IMETHODIMP
DateImpl::EqualsNode(nsIRDFNode* node, bool* result)
{
nsresult rv;
nsIRDFDate* date;
if (NS_SUCCEEDED(node->QueryInterface(kIRDFDateIID, (void**) &date))) {
rv = EqualsDate(date, result);
NS_RELEASE(date);
}
else {
*result = false;
rv = NS_OK;
}
return rv;
}
NS_IMETHODIMP
DateImpl::GetValue(PRTime *value)
{
NS_ASSERTION(value, "null ptr");
if (! value)
return NS_ERROR_NULL_POINTER;
*value = mValue;
return NS_OK;
}
nsresult
DateImpl::EqualsDate(nsIRDFDate* date, bool* result)
{
NS_ASSERTION(date && result, "null ptr");
if (!date || !result)
return NS_ERROR_NULL_POINTER;
nsresult rv;
PRTime p;
if (NS_FAILED(rv = date->GetValue(&p)))
return rv;
*result = p == mValue;
return NS_OK;
}
////////////////////////////////////////////////////////////////////////
// IntImpl
//
class IntImpl : public nsIRDFInt {
public:
explicit IntImpl(int32_t s);
// nsISupports
NS_DECL_ISUPPORTS
// nsIRDFNode
NS_DECL_NSIRDFNODE
// nsIRDFInt
NS_IMETHOD GetValue(int32_t *value) override;
private:
virtual ~IntImpl();
nsresult EqualsInt(nsIRDFInt* value, bool* result);
int32_t mValue;
};
IntImpl::IntImpl(int32_t s)
: mValue(s)
{
RDFServiceImpl::gRDFService->RegisterInt(this);
NS_ADDREF(RDFServiceImpl::gRDFService);
}
IntImpl::~IntImpl()
{
RDFServiceImpl::gRDFService->UnregisterInt(this);
// Use NS_RELEASE2() here, because we want to decrease the
// refcount, but not null out the gRDFService pointer (which is
// what a vanilla NS_RELEASE() would do).
nsrefcnt refcnt;
NS_RELEASE2(RDFServiceImpl::gRDFService, refcnt);
}
NS_IMPL_ADDREF(IntImpl)
NS_IMPL_RELEASE(IntImpl)
nsresult
IntImpl::QueryInterface(REFNSIID iid, void** result)
{
if (! result)
return NS_ERROR_NULL_POINTER;
*result = nullptr;
if (iid.Equals(kIRDFIntIID) ||
iid.Equals(kIRDFNodeIID) ||
iid.Equals(kISupportsIID)) {
*result = static_cast<nsIRDFInt*>(this);
AddRef();
return NS_OK;
}
return NS_NOINTERFACE;
}
NS_IMETHODIMP
IntImpl::EqualsNode(nsIRDFNode* node, bool* result)
{
nsresult rv;
nsIRDFInt* intValue;
if (NS_SUCCEEDED(node->QueryInterface(kIRDFIntIID, (void**) &intValue))) {
rv = EqualsInt(intValue, result);
NS_RELEASE(intValue);
}
else {
*result = false;
rv = NS_OK;
}
return rv;
}
NS_IMETHODIMP
IntImpl::GetValue(int32_t *value)
{
NS_ASSERTION(value, "null ptr");
if (! value)
return NS_ERROR_NULL_POINTER;
*value = mValue;
return NS_OK;
}
nsresult
IntImpl::EqualsInt(nsIRDFInt* intValue, bool* result)
{
NS_ASSERTION(intValue && result, "null ptr");
if (!intValue || !result)
return NS_ERROR_NULL_POINTER;
nsresult rv;
int32_t p;
if (NS_FAILED(rv = intValue->GetValue(&p)))
return rv;
*result = (p == mValue);
return NS_OK;
}
////////////////////////////////////////////////////////////////////////
// RDFServiceImpl
RDFServiceImpl*
RDFServiceImpl::gRDFService;
RDFServiceImpl::RDFServiceImpl()
: mNamedDataSources(nullptr)
, mResources(&gResourceTableOps, sizeof(ResourceHashEntry))
, mLiterals(&gLiteralTableOps, sizeof(LiteralHashEntry))
, mInts(&gIntTableOps, sizeof(IntHashEntry))
, mDates(&gDateTableOps, sizeof(DateHashEntry))
, mBlobs(&gBlobTableOps, sizeof(BlobHashEntry))
{
gRDFService = this;
}
nsresult
RDFServiceImpl::Init()
{
nsresult rv;
mNamedDataSources = PL_NewHashTable(23,
PL_HashString,
PL_CompareStrings,
PL_CompareValues,
&dataSourceHashAllocOps, nullptr);
if (! mNamedDataSources)
return NS_ERROR_OUT_OF_MEMORY;
mDefaultResourceFactory = do_GetClassObject(kRDFDefaultResourceCID, &rv);
NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get default resource factory");
if (NS_FAILED(rv)) return rv;
if (! gLog)
gLog = PR_NewLogModule("nsRDFService");
return NS_OK;
}
RDFServiceImpl::~RDFServiceImpl()
{
if (mNamedDataSources) {
PL_HashTableDestroy(mNamedDataSources);
mNamedDataSources = nullptr;
}
gRDFService = nullptr;
}
// static
nsresult
RDFServiceImpl::CreateSingleton(nsISupports* aOuter,
const nsIID& aIID, void **aResult)
{
NS_ENSURE_NO_AGGREGATION(aOuter);
if (gRDFService) {
NS_ERROR("Trying to create RDF serviec twice.");
return gRDFService->QueryInterface(aIID, aResult);
}
nsRefPtr<RDFServiceImpl> serv = new RDFServiceImpl();
nsresult rv = serv->Init();
if (NS_FAILED(rv))
return rv;
return serv->QueryInterface(aIID, aResult);
}
NS_IMPL_ISUPPORTS(RDFServiceImpl, nsIRDFService, nsISupportsWeakReference)
// Per RFC2396.
static const uint8_t
kLegalSchemeChars[] = {
// ASCII Bits Ordered Hex
// 01234567 76543210
0x00, // 00-07
0x00, // 08-0F
0x00, // 10-17
0x00, // 18-1F
0x00, // 20-27 !"#$%&' 00000000 00000000
0x28, // 28-2F ()*+,-./ 00010100 00101000 0x28
0xff, // 30-37 01234567 11111111 11111111 0xFF
0x03, // 38-3F 89:;<=>? 11000000 00000011 0x03
0xfe, // 40-47 @ABCDEFG 01111111 11111110 0xFE
0xff, // 48-4F HIJKLMNO 11111111 11111111 0xFF
0xff, // 50-57 PQRSTUVW 11111111 11111111 0xFF
0x87, // 58-5F XYZ[\]^_ 11100001 10000111 0x87
0xfe, // 60-67 `abcdefg 01111111 11111110 0xFE
0xff, // 68-6F hijklmno 11111111 11111111 0xFF
0xff, // 70-77 pqrstuvw 11111111 11111111 0xFF
0x07, // 78-7F xyz{|}~ 11100000 00000111 0x07
0x00, 0x00, 0x00, 0x00, // >= 80
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00
};
static inline bool
IsLegalSchemeCharacter(const char aChar)
{
uint8_t mask = kLegalSchemeChars[aChar >> 3];
uint8_t bit = 1u << (aChar & 0x7);
return bool((mask & bit) != 0);
}
NS_IMETHODIMP
RDFServiceImpl::GetResource(const nsACString& aURI, nsIRDFResource** aResource)
{
// Sanity checks
NS_PRECONDITION(aResource != nullptr, "null ptr");
NS_PRECONDITION(!aURI.IsEmpty(), "URI is empty");
if (! aResource)
return NS_ERROR_NULL_POINTER;
if (aURI.IsEmpty())
return NS_ERROR_INVALID_ARG;
const nsAFlatCString& flatURI = PromiseFlatCString(aURI);
MOZ_LOG(gLog, LogLevel::Debug, ("rdfserv get-resource %s", flatURI.get()));
// First, check the cache to see if we've already created and
// registered this thing.
PLDHashEntryHdr *hdr = PL_DHashTableSearch(&mResources, flatURI.get());
if (hdr) {
ResourceHashEntry *entry = static_cast<ResourceHashEntry *>(hdr);
NS_ADDREF(*aResource = entry->mResource);
return NS_OK;
}
// Nope. So go to the repository to create it.
// Compute the scheme of the URI. Scan forward until we either:
//
// 1. Reach the end of the string
// 2. Encounter a non-alpha character
// 3. Encouter a colon.
//
// If we encounter a colon _before_ encountering a non-alpha
// character, then assume it's the scheme.
//
// XXX Although it's really not correct, we'll allow underscore
// characters ('_'), too.
nsACString::const_iterator p, end;
aURI.BeginReading(p);
aURI.EndReading(end);
while (p != end && IsLegalSchemeCharacter(*p))
++p;
nsresult rv;
nsCOMPtr<nsIFactory> factory;
nsACString::const_iterator begin;
aURI.BeginReading(begin);
if (*p == ':') {
// There _was_ a scheme. First see if it's the same scheme
// that we just tried to use...
if (mLastFactory && mLastURIPrefix.Equals(Substring(begin, p)))
factory = mLastFactory;
else {
// Try to find a factory using the component manager.
nsACString::const_iterator begin;
aURI.BeginReading(begin);
nsAutoCString contractID;
contractID = NS_LITERAL_CSTRING(NS_RDF_RESOURCE_FACTORY_CONTRACTID_PREFIX) +
Substring(begin, p);
factory = do_GetClassObject(contractID.get());
if (factory) {
// Store the factory in our one-element cache.
if (p != begin) {
mLastFactory = factory;
mLastURIPrefix = Substring(begin, p);
}
}
}
}
if (! factory) {
// fall through to using the "default" resource factory if either:
//
// 1. The URI didn't have a scheme, or
// 2. There was no resource factory registered for the scheme.
factory = mDefaultResourceFactory;
// Store the factory in our one-element cache.
if (p != begin) {
mLastFactory = factory;
mLastURIPrefix = Substring(begin, p);
}
}
nsIRDFResource *result;
rv = factory->CreateInstance(nullptr, NS_GET_IID(nsIRDFResource), (void**) &result);
if (NS_FAILED(rv)) return rv;
// Now initialize it with its URI. At this point, the resource
// implementation should register itself with the RDF service.
rv = result->Init(flatURI.get());
if (NS_FAILED(rv)) {
NS_ERROR("unable to initialize resource");
NS_RELEASE(result);
return rv;
}
*aResource = result; // already refcounted from repository
return rv;
}
NS_IMETHODIMP
RDFServiceImpl::GetUnicodeResource(const nsAString& aURI, nsIRDFResource** aResource)
{
return GetResource(NS_ConvertUTF16toUTF8(aURI), aResource);
}
NS_IMETHODIMP
RDFServiceImpl::GetAnonymousResource(nsIRDFResource** aResult)
{
static uint32_t gCounter = 0;
static char gChars[] = "0123456789abcdef"
"ghijklmnopqrstuv"
"wxyzABCDEFGHIJKL"
"MNOPQRSTUVWXYZ.+";
static int32_t kMask = 0x003f;
static int32_t kShift = 6;
if (! gCounter) {
// Start it at a semi-unique value, just to minimize the
// chance that we get into a situation where
//
// 1. An anonymous resource gets serialized out in a graph
// 2. Reboot
// 3. The same anonymous resource gets requested, and refers
// to something completely different.
// 4. The serialization is read back in.
gCounter = uint32_t(PR_Now());
}
nsresult rv;
nsAutoCString s;
do {
// Ugh, this is a really sloppy way to do this; I copied the
// implementation from the days when it lived outside the RDF
// service. Now that it's a member we can be more cleverer.
s.Truncate();
s.AppendLiteral("rdf:#$");
uint32_t id = ++gCounter;
while (id) {
char ch = gChars[(id & kMask)];
s.Append(ch);
id >>= kShift;
}
nsIRDFResource* resource;
rv = GetResource(s, &resource);
if (NS_FAILED(rv)) return rv;
// XXX an ugly but effective way to make sure that this
// resource is really unique in the world.
resource->AddRef();
nsrefcnt refcnt = resource->Release();
if (refcnt == 1) {
*aResult = resource;
break;
}
NS_RELEASE(resource);
} while (1);
return NS_OK;
}
NS_IMETHODIMP
RDFServiceImpl::GetLiteral(const char16_t* aValue, nsIRDFLiteral** aLiteral)
{
NS_PRECONDITION(aValue != nullptr, "null ptr");
if (! aValue)
return NS_ERROR_NULL_POINTER;
NS_PRECONDITION(aLiteral != nullptr, "null ptr");
if (! aLiteral)
return NS_ERROR_NULL_POINTER;
// See if we have one already cached
PLDHashEntryHdr *hdr = PL_DHashTableSearch(&mLiterals, aValue);
if (hdr) {
LiteralHashEntry *entry = static_cast<LiteralHashEntry *>(hdr);
NS_ADDREF(*aLiteral = entry->mLiteral);
return NS_OK;
}
// Nope. Create a new one
return LiteralImpl::Create(aValue, aLiteral);
}
NS_IMETHODIMP
RDFServiceImpl::GetDateLiteral(PRTime aTime, nsIRDFDate** aResult)
{
// See if we have one already cached
PLDHashEntryHdr *hdr = PL_DHashTableSearch(&mDates, &aTime);
if (hdr) {
DateHashEntry *entry = static_cast<DateHashEntry *>(hdr);
NS_ADDREF(*aResult = entry->mDate);
return NS_OK;
}
DateImpl* result = new DateImpl(aTime);
if (! result)
return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(*aResult = result);
return NS_OK;
}
NS_IMETHODIMP
RDFServiceImpl::GetIntLiteral(int32_t aInt, nsIRDFInt** aResult)
{
// See if we have one already cached
PLDHashEntryHdr *hdr = PL_DHashTableSearch(&mInts, &aInt);
if (hdr) {
IntHashEntry *entry = static_cast<IntHashEntry *>(hdr);
NS_ADDREF(*aResult = entry->mInt);
return NS_OK;
}
IntImpl* result = new IntImpl(aInt);
if (! result)
return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(*aResult = result);
return NS_OK;
}
NS_IMETHODIMP
RDFServiceImpl::GetBlobLiteral(const uint8_t *aBytes, int32_t aLength,
nsIRDFBlob **aResult)
{
BlobImpl::Data key = { aLength, const_cast<uint8_t *>(aBytes) };
PLDHashEntryHdr *hdr = PL_DHashTableSearch(&mBlobs, &key);
if (hdr) {
BlobHashEntry *entry = static_cast<BlobHashEntry *>(hdr);
NS_ADDREF(*aResult = entry->mBlob);
return NS_OK;
}
BlobImpl *result = new BlobImpl(aBytes, aLength);
if (! result)
return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(*aResult = result);
return NS_OK;
}
NS_IMETHODIMP
RDFServiceImpl::IsAnonymousResource(nsIRDFResource* aResource, bool* _result)
{
NS_PRECONDITION(aResource != nullptr, "null ptr");
if (! aResource)
return NS_ERROR_NULL_POINTER;
nsresult rv;
const char* uri;
rv = aResource->GetValueConst(&uri);
if (NS_FAILED(rv)) return rv;
if ((uri[0] == 'r') &&
(uri[1] == 'd') &&
(uri[2] == 'f') &&
(uri[3] == ':') &&
(uri[4] == '#') &&
(uri[5] == '$')) {
*_result = true;
}
else {
*_result = false;
}
return NS_OK;
}
NS_IMETHODIMP
RDFServiceImpl::RegisterResource(nsIRDFResource* aResource, bool aReplace)
{
NS_PRECONDITION(aResource != nullptr, "null ptr");
if (! aResource)
return NS_ERROR_NULL_POINTER;
nsresult rv;
const char* uri;
rv = aResource->GetValueConst(&uri);
NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get URI from resource");
if (NS_FAILED(rv)) return rv;
NS_ASSERTION(uri != nullptr, "resource has no URI");
if (! uri)
return NS_ERROR_NULL_POINTER;
PLDHashEntryHdr *hdr = PL_DHashTableSearch(&mResources, uri);
if (hdr) {
if (!aReplace) {
NS_WARNING("resource already registered, and replace not specified");
return NS_ERROR_FAILURE; // already registered
}
// N.B., we do _not_ release the original resource because we
// only ever held a weak reference to it. We simply replace
// it.
MOZ_LOG(gLog, LogLevel::Debug,
("rdfserv replace-resource [%p] <-- [%p] %s",
static_cast<ResourceHashEntry *>(hdr)->mResource,
aResource, (const char*) uri));
}
else {
hdr = PL_DHashTableAdd(&mResources, uri, fallible);
if (! hdr)
return NS_ERROR_OUT_OF_MEMORY;
MOZ_LOG(gLog, LogLevel::Debug,
("rdfserv register-resource [%p] %s",
aResource, (const char*) uri));
}
// N.B., we only hold a weak reference to the resource: that way,
// the resource can be destroyed when the last refcount goes
// away. The single addref that the CreateResource() call made
// will be owned by the callee.
ResourceHashEntry *entry = static_cast<ResourceHashEntry *>(hdr);
entry->mResource = aResource;
entry->mKey = uri;
return NS_OK;
}
NS_IMETHODIMP
RDFServiceImpl::UnregisterResource(nsIRDFResource* aResource)
{
NS_PRECONDITION(aResource != nullptr, "null ptr");
if (! aResource)
return NS_ERROR_NULL_POINTER;
nsresult rv;
const char* uri;
rv = aResource->GetValueConst(&uri);
if (NS_FAILED(rv)) return rv;
NS_ASSERTION(uri != nullptr, "resource has no URI");
if (! uri)
return NS_ERROR_UNEXPECTED;
MOZ_LOG(gLog, LogLevel::Debug,
("rdfserv unregister-resource [%p] %s",
aResource, (const char*) uri));
#ifdef DEBUG
if (!PL_DHashTableSearch(&mResources, uri))
NS_WARNING("resource was never registered");
#endif
PL_DHashTableRemove(&mResources, uri);
return NS_OK;
}
NS_IMETHODIMP
RDFServiceImpl::RegisterDataSource(nsIRDFDataSource* aDataSource, bool aReplace)
{
NS_PRECONDITION(aDataSource != nullptr, "null ptr");
if (! aDataSource)
return NS_ERROR_NULL_POINTER;
nsresult rv;
nsXPIDLCString uri;
rv = aDataSource->GetURI(getter_Copies(uri));
if (NS_FAILED(rv)) return rv;
PLHashEntry** hep =
PL_HashTableRawLookup(mNamedDataSources, (*mNamedDataSources->keyHash)(uri), uri);
if (*hep) {
if (! aReplace)
return NS_ERROR_FAILURE; // already registered
// N.B., we only hold a weak reference to the datasource, so
// just replace the old with the new and don't touch any
// refcounts.
MOZ_LOG(gLog, LogLevel::Debug,
("rdfserv replace-datasource [%p] <-- [%p] %s",
(*hep)->value, aDataSource, (const char*) uri));
(*hep)->value = aDataSource;
}
else {
const char* key = PL_strdup(uri);
if (! key)
return NS_ERROR_OUT_OF_MEMORY;
PL_HashTableAdd(mNamedDataSources, key, aDataSource);
MOZ_LOG(gLog, LogLevel::Debug,
("rdfserv register-datasource [%p] %s",
aDataSource, (const char*) uri));
// N.B., we only hold a weak reference to the datasource, so don't
// addref.
}
return NS_OK;
}
NS_IMETHODIMP
RDFServiceImpl::UnregisterDataSource(nsIRDFDataSource* aDataSource)
{
NS_PRECONDITION(aDataSource != nullptr, "null ptr");
if (! aDataSource)
return NS_ERROR_NULL_POINTER;
nsresult rv;
nsXPIDLCString uri;
rv = aDataSource->GetURI(getter_Copies(uri));
if (NS_FAILED(rv)) return rv;
//NS_ASSERTION(uri != nullptr, "datasource has no URI");
if (! uri)
return NS_ERROR_UNEXPECTED;
PLHashEntry** hep =
PL_HashTableRawLookup(mNamedDataSources, (*mNamedDataSources->keyHash)(uri), uri);
// It may well be that this datasource was never registered. If
// so, don't unregister it.
if (! *hep || ((*hep)->value != aDataSource))
return NS_OK;
// N.B., we only held a weak reference to the datasource, so we
// don't release here.
PL_HashTableRawRemove(mNamedDataSources, hep, *hep);
MOZ_LOG(gLog, LogLevel::Debug,
("rdfserv unregister-datasource [%p] %s",
aDataSource, (const char*) uri));
return NS_OK;
}
NS_IMETHODIMP
RDFServiceImpl::GetDataSource(const char* aURI, nsIRDFDataSource** aDataSource)
{
// Use the other GetDataSource and ask for a non-blocking Refresh.
// If you wanted it loaded synchronously, then you should've tried to do it
// yourself, or used GetDataSourceBlocking.
return GetDataSource( aURI, false, aDataSource );
}
NS_IMETHODIMP
RDFServiceImpl::GetDataSourceBlocking(const char* aURI, nsIRDFDataSource** aDataSource)
{
// Use GetDataSource and ask for a blocking Refresh.
return GetDataSource( aURI, true, aDataSource );
}
nsresult
RDFServiceImpl::GetDataSource(const char* aURI, bool aBlock, nsIRDFDataSource** aDataSource)
{
NS_PRECONDITION(aURI != nullptr, "null ptr");
if (! aURI)
return NS_ERROR_NULL_POINTER;
nsresult rv;
// Attempt to canonify the URI before we look for it in the
// cache. We won't bother doing this on `rdf:' URIs to avoid
// useless (and expensive) protocol handler lookups.
nsAutoCString spec(aURI);
if (!StringBeginsWith(spec, NS_LITERAL_CSTRING("rdf:"))) {
nsCOMPtr<nsIURI> uri;
NS_NewURI(getter_AddRefs(uri), spec);
if (uri)
uri->GetSpec(spec);
}
// First, check the cache to see if we already have this
// datasource loaded and initialized.
{
nsIRDFDataSource* cached =
static_cast<nsIRDFDataSource*>(PL_HashTableLookup(mNamedDataSources, spec.get()));
if (cached) {
NS_ADDREF(cached);
*aDataSource = cached;
return NS_OK;
}
}
// Nope. So go to the repository to try to create it.
nsCOMPtr<nsIRDFDataSource> ds;
if (StringBeginsWith(spec, NS_LITERAL_CSTRING("rdf:"))) {
// It's a built-in data source. Convert it to a contract ID.
nsAutoCString contractID(
NS_LITERAL_CSTRING(NS_RDF_DATASOURCE_CONTRACTID_PREFIX) +
Substring(spec, 4, spec.Length() - 4));
// Strip params to get ``base'' contractID for data source.
int32_t p = contractID.FindChar(char16_t('&'));
if (p >= 0)
contractID.Truncate(p);
ds = do_GetService(contractID.get(), &rv);
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsIRDFRemoteDataSource> remote = do_QueryInterface(ds);
if (remote) {
rv = remote->Init(spec.get());
if (NS_FAILED(rv)) return rv;
}
}
else {
// Try to load this as an RDF/XML data source
ds = do_CreateInstance(kRDFXMLDataSourceCID, &rv);
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsIRDFRemoteDataSource> remote(do_QueryInterface(ds));
NS_ASSERTION(remote, "not a remote RDF/XML data source!");
if (! remote) return NS_ERROR_UNEXPECTED;
rv = remote->Init(spec.get());
if (NS_FAILED(rv)) return rv;
rv = remote->Refresh(aBlock);
if (NS_FAILED(rv)) return rv;
}
*aDataSource = ds;
NS_ADDREF(*aDataSource);
return NS_OK;
}
////////////////////////////////////////////////////////////////////////
nsresult
RDFServiceImpl::RegisterLiteral(nsIRDFLiteral* aLiteral)
{
const char16_t* value;
aLiteral->GetValueConst(&value);
NS_ASSERTION(!PL_DHashTableSearch(&mLiterals, value),
"literal already registered");
PLDHashEntryHdr *hdr = PL_DHashTableAdd(&mLiterals, value, fallible);
if (! hdr)
return NS_ERROR_OUT_OF_MEMORY;
LiteralHashEntry *entry = static_cast<LiteralHashEntry *>(hdr);
// N.B., we only hold a weak reference to the literal: that
// way, the literal can be destroyed when the last refcount
// goes away. The single addref that the CreateLiteral() call
// made will be owned by the callee.
entry->mLiteral = aLiteral;
entry->mKey = value;
MOZ_LOG(gLog, LogLevel::Debug,
("rdfserv register-literal [%p] %s",
aLiteral, (const char16_t*) value));
return NS_OK;
}
nsresult
RDFServiceImpl::UnregisterLiteral(nsIRDFLiteral* aLiteral)
{
const char16_t* value;
aLiteral->GetValueConst(&value);
NS_ASSERTION(PL_DHashTableSearch(&mLiterals, value),
"literal was never registered");
PL_DHashTableRemove(&mLiterals, value);
// N.B. that we _don't_ release the literal: we only held a weak
// reference to it in the hashtable.
MOZ_LOG(gLog, LogLevel::Debug,
("rdfserv unregister-literal [%p] %s",
aLiteral, (const char16_t*) value));
return NS_OK;
}
//----------------------------------------------------------------------
nsresult
RDFServiceImpl::RegisterInt(nsIRDFInt* aInt)
{
int32_t value;
aInt->GetValue(&value);
NS_ASSERTION(!PL_DHashTableSearch(&mInts, &value),
"int already registered");
PLDHashEntryHdr *hdr = PL_DHashTableAdd(&mInts, &value, fallible);
if (! hdr)
return NS_ERROR_OUT_OF_MEMORY;
IntHashEntry *entry = static_cast<IntHashEntry *>(hdr);
// N.B., we only hold a weak reference to the literal: that
// way, the literal can be destroyed when the last refcount
// goes away. The single addref that the CreateInt() call
// made will be owned by the callee.
entry->mInt = aInt;
entry->mKey = value;
MOZ_LOG(gLog, LogLevel::Debug,
("rdfserv register-int [%p] %d",
aInt, value));
return NS_OK;
}
nsresult
RDFServiceImpl::UnregisterInt(nsIRDFInt* aInt)
{
int32_t value;
aInt->GetValue(&value);
NS_ASSERTION(PL_DHashTableSearch(&mInts, &value),
"int was never registered");
PL_DHashTableRemove(&mInts, &value);
// N.B. that we _don't_ release the literal: we only held a weak
// reference to it in the hashtable.
MOZ_LOG(gLog, LogLevel::Debug,
("rdfserv unregister-int [%p] %d",
aInt, value));
return NS_OK;
}
//----------------------------------------------------------------------
nsresult
RDFServiceImpl::RegisterDate(nsIRDFDate* aDate)
{
PRTime value;
aDate->GetValue(&value);
NS_ASSERTION(!PL_DHashTableSearch(&mDates, &value),
"date already registered");
PLDHashEntryHdr *hdr = PL_DHashTableAdd(&mDates, &value, fallible);
if (! hdr)
return NS_ERROR_OUT_OF_MEMORY;
DateHashEntry *entry = static_cast<DateHashEntry *>(hdr);
// N.B., we only hold a weak reference to the literal: that
// way, the literal can be destroyed when the last refcount
// goes away. The single addref that the CreateDate() call
// made will be owned by the callee.
entry->mDate = aDate;
entry->mKey = value;
MOZ_LOG(gLog, LogLevel::Debug,
("rdfserv register-date [%p] %ld",
aDate, value));
return NS_OK;
}
nsresult
RDFServiceImpl::UnregisterDate(nsIRDFDate* aDate)
{
PRTime value;
aDate->GetValue(&value);
NS_ASSERTION(PL_DHashTableSearch(&mDates, &value),
"date was never registered");
PL_DHashTableRemove(&mDates, &value);
// N.B. that we _don't_ release the literal: we only held a weak
// reference to it in the hashtable.
MOZ_LOG(gLog, LogLevel::Debug,
("rdfserv unregister-date [%p] %ld",
aDate, value));
return NS_OK;
}
nsresult
RDFServiceImpl::RegisterBlob(BlobImpl *aBlob)
{
NS_ASSERTION(!PL_DHashTableSearch(&mBlobs, &aBlob->mData),
"blob already registered");
PLDHashEntryHdr *hdr = PL_DHashTableAdd(&mBlobs, &aBlob->mData, fallible);
if (! hdr)
return NS_ERROR_OUT_OF_MEMORY;
BlobHashEntry *entry = static_cast<BlobHashEntry *>(hdr);
// N.B., we only hold a weak reference to the literal: that
// way, the literal can be destroyed when the last refcount
// goes away. The single addref that the CreateInt() call
// made will be owned by the callee.
entry->mBlob = aBlob;
MOZ_LOG(gLog, LogLevel::Debug,
("rdfserv register-blob [%p] %s",
aBlob, aBlob->mData.mBytes));
return NS_OK;
}
nsresult
RDFServiceImpl::UnregisterBlob(BlobImpl *aBlob)
{
NS_ASSERTION(PL_DHashTableSearch(&mBlobs, &aBlob->mData),
"blob was never registered");
PL_DHashTableRemove(&mBlobs, &aBlob->mData);
// N.B. that we _don't_ release the literal: we only held a weak
// reference to it in the hashtable.
MOZ_LOG(gLog, LogLevel::Debug,
("rdfserv unregister-blob [%p] %s",
aBlob, aBlob->mData.mBytes));
return NS_OK;
}