mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-05 08:35:26 +00:00
568 lines
17 KiB
C++
568 lines
17 KiB
C++
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/*
|
|
* The contents of this file are subject to the Netscape Public
|
|
* License Version 1.1 (the "License"); you may not use this file
|
|
* except in compliance with the License. You may obtain a copy of
|
|
* the License at http://www.mozilla.org/NPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS
|
|
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
|
* implied. See the License for the specific language governing
|
|
* rights and limitations under the License.
|
|
*
|
|
* The Original Code is mozilla.org code.
|
|
*
|
|
* The Initial Developer of the Original Code is Netscape
|
|
* Communications Corporation. Portions created by Netscape are
|
|
* Copyright (C) 2000 Netscape Communications Corporation. All
|
|
* Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
* Scott Collins <scc@netscape.com>
|
|
*/
|
|
|
|
#include "nsICategoryManager.h"
|
|
|
|
#include "nsCOMPtr.h"
|
|
#include "nsHashtable.h"
|
|
#include "nsIFactory.h"
|
|
#include "nsIRegistry.h"
|
|
#include "nsISupportsPrimitives.h"
|
|
#include "nsComponentManager.h"
|
|
|
|
#include "nsHashtableEnumerator.h"
|
|
#include "nsEnumeratorUtils.h"
|
|
|
|
|
|
/*
|
|
CategoryDatabase
|
|
contains 0 or more 1-1 mappings of string to Category
|
|
each Category contains 0 or more 1-1 mappings of string keys to string values
|
|
|
|
In other words, the CategoryDatabase is a tree, whose root is a hashtable.
|
|
Internal nodes (or Categories) are hashtables.
|
|
Leaf nodes are strings.
|
|
*/
|
|
|
|
|
|
static
|
|
NS_IMETHODIMP
|
|
ExtractKeyString( nsHashKey* key, void*, void*, nsISupports** _retval )
|
|
/*
|
|
...works with |nsHashtableEnumerator| to make the hash keys enumerable.
|
|
*/
|
|
{
|
|
nsresult status;
|
|
nsCOMPtr<nsISupportsString> obj = do_CreateInstance(NS_SUPPORTS_STRING_PROGID, &status);
|
|
if ( obj )
|
|
{
|
|
// BULLSHIT ALERT: use |nsCAutoString| to deflate to single-byte until I can add, e.g.,
|
|
// |nsCStringKey| to hashtable
|
|
const nsString& s = NS_STATIC_CAST(nsStringKey*, key)->GetString();
|
|
status = obj->SetDataWithLength(s.Length(), nsCAutoString(s));
|
|
}
|
|
|
|
*_retval = obj;
|
|
NS_IF_ADDREF(*_retval);
|
|
return status;
|
|
}
|
|
|
|
|
|
|
|
typedef nsCString LeafNode;
|
|
|
|
/*
|
|
Our interior nodes are hashtables whose elements are |LeafNode|s,
|
|
and we need a suitable destruction function to register with a
|
|
given (interior node) hashtable for destroying its (|LeafNode|) elements.
|
|
*/
|
|
static
|
|
PRBool
|
|
Destroy_LeafNode( nsHashKey*, void* aElement, void* )
|
|
{
|
|
delete NS_STATIC_CAST(LeafNode*, aElement);
|
|
return PR_TRUE;
|
|
}
|
|
|
|
|
|
class CategoryNode
|
|
: public nsObjectHashtable
|
|
{
|
|
public:
|
|
CategoryNode()
|
|
: nsObjectHashtable(0, 0, Destroy_LeafNode, 0)
|
|
{
|
|
// Nothing else to do here...
|
|
}
|
|
|
|
LeafNode* find_leaf( const char* );
|
|
};
|
|
|
|
LeafNode*
|
|
CategoryNode::find_leaf( const char* aLeafName )
|
|
{
|
|
nsStringKey leafNameKey(aLeafName);
|
|
return NS_STATIC_CAST(LeafNode*, Get(&leafNameKey));
|
|
}
|
|
|
|
/*
|
|
We keep a hashtable of hashtables, therefore, we need a suitable
|
|
destruction function to register with the outer table for destroying
|
|
elements which are the inner tables.
|
|
*/
|
|
static
|
|
PRBool
|
|
Destroy_CategoryNode( nsHashKey*, void* aElement, void* )
|
|
{
|
|
delete NS_STATIC_CAST(CategoryNode*, aElement);
|
|
return PR_TRUE;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class nsCategoryManager
|
|
: public nsICategoryManager,
|
|
public nsObjectHashtable
|
|
{
|
|
private:
|
|
friend class nsCategoryManagerFactory;
|
|
nsCategoryManager();
|
|
nsresult initialize();
|
|
// Warning: whoever creates an instance must call |initialize()| before any other method
|
|
|
|
public:
|
|
virtual ~nsCategoryManager();
|
|
|
|
NS_DECL_ISUPPORTS
|
|
NS_DECL_NSICATEGORYMANAGER
|
|
|
|
private:
|
|
CategoryNode* find_category( const char* );
|
|
nsresult persist( const char* aCategoryName, const char* aKey, const char* aValue );
|
|
nsresult dont_persist( const char* aCategoryName, const char* aKey );
|
|
|
|
private:
|
|
// |mRegistry != 0| after |initialize()|... I can't live without it
|
|
nsCOMPtr<nsIRegistry> mRegistry;
|
|
nsRegistryKey mCategoriesRegistryKey;
|
|
};
|
|
|
|
NS_IMPL_ISUPPORTS1(nsCategoryManager, nsICategoryManager)
|
|
|
|
nsCategoryManager::nsCategoryManager()
|
|
: nsObjectHashtable(0, 0, Destroy_CategoryNode, 0)
|
|
{
|
|
NS_INIT_REFCNT();
|
|
}
|
|
|
|
nsresult
|
|
nsCategoryManager::initialize()
|
|
{
|
|
const char* kCategoriesRegistryPath = "software/mozilla/XPCOM/categories";
|
|
// Alas, this is kind of buried down here, but you can't put constant strings in a class declaration... oh, well
|
|
|
|
|
|
// Get a pointer to the registry, and get it open and ready for us
|
|
|
|
nsresult status;
|
|
if ( mRegistry = do_GetService(NS_REGISTRY_PROGID, &status) )
|
|
if ( NS_SUCCEEDED(status = mRegistry->OpenWellKnownRegistry(nsIRegistry::ApplicationComponentRegistry)) )
|
|
if ( (status = mRegistry->GetSubtree(nsIRegistry::Common, kCategoriesRegistryPath, &mCategoriesRegistryKey)) == NS_ERROR_REG_NOT_FOUND )
|
|
status = mRegistry->AddSubtree(nsIRegistry::Common, kCategoriesRegistryPath, &mCategoriesRegistryKey);
|
|
|
|
// All right, we've got the registry now (or not), that's the important thing.
|
|
// Returning an error will cause callers to destroy me. So not getting the registry
|
|
// is worth returning an error for (I can't live without it). Now we'll load in our
|
|
// persistent data from the registry, but that's _not_ worth getting killed over, so
|
|
// don't save any errors from this process.
|
|
|
|
// Now load the registry data
|
|
|
|
if ( NS_SUCCEEDED(status) )
|
|
{
|
|
nsCOMPtr<nsIEnumerator> keys;
|
|
mRegistry->EnumerateSubtrees(mCategoriesRegistryKey, getter_AddRefs(keys));
|
|
for ( keys->First(); keys->IsDone() == NS_ENUMERATOR_FALSE; keys->Next() )
|
|
{
|
|
nsXPIDLCString categoryName;
|
|
nsRegistryKey categoryKey;
|
|
|
|
{
|
|
nsCOMPtr<nsISupports> supportsNode;
|
|
keys->CurrentItem(getter_AddRefs(supportsNode));
|
|
|
|
nsCOMPtr<nsIRegistryNode> registryNode = do_QueryInterface(supportsNode);
|
|
registryNode->GetName(getter_Copies(categoryName));
|
|
registryNode->GetKey(&categoryKey);
|
|
}
|
|
|
|
nsCOMPtr<nsIEnumerator> values;
|
|
mRegistry->EnumerateValues(categoryKey, getter_AddRefs(values));
|
|
for ( values->First(); values->IsDone() == NS_ENUMERATOR_FALSE; values->Next() )
|
|
{
|
|
nsXPIDLCString entryName;
|
|
|
|
{
|
|
nsCOMPtr<nsISupports> supportsValue;
|
|
values->CurrentItem(getter_AddRefs(supportsValue));
|
|
|
|
nsCOMPtr<nsIRegistryValue> registryValue = do_QueryInterface(supportsValue);
|
|
registryValue->GetName(getter_Copies(entryName));
|
|
}
|
|
|
|
nsXPIDLCString value;
|
|
mRegistry->GetStringUTF8(categoryKey, entryName, getter_Copies(value));
|
|
AddCategoryEntry(categoryName, entryName, value, PR_FALSE, PR_FALSE, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
nsCategoryManager::~nsCategoryManager()
|
|
{
|
|
}
|
|
|
|
CategoryNode*
|
|
nsCategoryManager::find_category( const char* aCategoryName )
|
|
{
|
|
nsStringKey categoryNameKey(aCategoryName);
|
|
return NS_STATIC_CAST(CategoryNode*, Get(&categoryNameKey));
|
|
}
|
|
|
|
nsresult
|
|
nsCategoryManager::persist( const char* aCategoryName, const char* aKey, const char* aValue )
|
|
{
|
|
NS_ASSERTION(mRegistry, "mRegistry is NULL!");
|
|
|
|
nsRegistryKey categoryRegistryKey;
|
|
nsresult status = mRegistry->GetSubtreeRaw(mCategoriesRegistryKey, aCategoryName, &categoryRegistryKey);
|
|
|
|
if ( status == NS_ERROR_REG_NOT_FOUND )
|
|
status = mRegistry->AddSubtreeRaw(mCategoriesRegistryKey, aCategoryName, &categoryRegistryKey);
|
|
|
|
if ( NS_SUCCEEDED(status) )
|
|
status = mRegistry->SetStringUTF8(categoryRegistryKey, aKey, aValue);
|
|
|
|
return status;
|
|
}
|
|
|
|
nsresult
|
|
nsCategoryManager::dont_persist( const char* aCategoryName, const char* aKey )
|
|
{
|
|
NS_ASSERTION(mRegistry, "mRegistry is NULL!");
|
|
|
|
nsRegistryKey categoryRegistryKey;
|
|
nsresult status = mRegistry->GetSubtreeRaw(mCategoriesRegistryKey, aCategoryName, &categoryRegistryKey);
|
|
|
|
if ( NS_SUCCEEDED(status) )
|
|
status = mRegistry->DeleteValue(categoryRegistryKey, aKey);
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
nsCategoryManager::GetCategoryEntry( const char *aCategoryName,
|
|
const char *aEntryName,
|
|
char **_retval )
|
|
{
|
|
// BULLSHIT ALERT: Category `handler's currently not implemented, so just call through
|
|
return GetCategoryEntryRaw(aCategoryName, aEntryName, _retval);
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
nsCategoryManager::GetCategoryEntryRaw( const char *aCategoryName,
|
|
const char *aEntryName,
|
|
char **_retval )
|
|
{
|
|
NS_ASSERTION(aCategoryName, "aCategoryName is NULL!");
|
|
NS_ASSERTION(aEntryName, "aEntryName is NULL!");
|
|
NS_ASSERTION(_retval, "_retval is NULL!");
|
|
|
|
nsresult status = NS_ERROR_NOT_AVAILABLE;
|
|
CategoryNode* category = find_category(aCategoryName);
|
|
if (category)
|
|
{
|
|
nsStringKey entryKey(aEntryName);
|
|
LeafNode* entry = NS_STATIC_CAST(LeafNode*, category->Get(&entryKey));
|
|
if (entry)
|
|
status = (*_retval = nsXPIDLCString::Copy(*entry)) ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
nsCategoryManager::AddCategoryEntry( const char *aCategoryName,
|
|
const char *aEntryName,
|
|
const char *aValue,
|
|
PRBool aPersist,
|
|
PRBool aReplace,
|
|
char **_retval )
|
|
{
|
|
NS_ASSERTION(aCategoryName, "aCategoryName is NULL!");
|
|
NS_ASSERTION(aEntryName, "aEntryName is NULL!");
|
|
NS_ASSERTION(aValue, "aValue is NULL!");
|
|
|
|
|
|
/*
|
|
Note: if |_retval| is |NULL|, I won't bother returning a copy
|
|
of the replaced value.
|
|
*/
|
|
|
|
if ( _retval )
|
|
*_retval = 0;
|
|
|
|
|
|
// Before we can insert a new entry, we'll need to
|
|
// find the |CategoryNode| to put it in...
|
|
CategoryNode* category;
|
|
if ( !(category = find_category(aCategoryName)) )
|
|
{
|
|
// That category doesn't exist yet; let's make it.
|
|
category = new CategoryNode;
|
|
nsStringKey categoryNameKey(aCategoryName);
|
|
Put(&categoryNameKey, category);
|
|
}
|
|
|
|
// See if this entry is already in this category
|
|
LeafNode* entry = category->find_leaf(aEntryName);
|
|
|
|
nsresult status = NS_OK;
|
|
if ( entry )
|
|
{
|
|
// If this entry is in the category already,
|
|
// then you better have said 'replace'!
|
|
|
|
if ( aReplace )
|
|
{
|
|
// return the value that we're replacing
|
|
if ( _retval )
|
|
*_retval = nsXPIDLCString::Copy(*entry);
|
|
}
|
|
else
|
|
status = NS_ERROR_INVALID_ARG;
|
|
}
|
|
|
|
if ( NS_SUCCEEDED(status) )
|
|
{
|
|
// If you didn't say 'replace', and there was already an entry there,
|
|
// then we can't put your value in, or make it persistent (see below)
|
|
entry = new LeafNode(aValue);
|
|
nsStringKey entryNameKey(aEntryName);
|
|
category->Put(&entryNameKey, entry);
|
|
|
|
// If you said 'persist' but not 'replace', and an entry
|
|
// got in the way ... sorry, you won't go into the persistent store.
|
|
// This was probably an error on your part. If this was _really_
|
|
// what you wanted to do, i.e., have a different value in the backing
|
|
// store than in the live database, then do it by setting the value
|
|
// with 'persist' and 'replace' and saving the returned old value, then
|
|
// restoring the old value with 'replace' but _not_ 'persist'.
|
|
|
|
if ( aPersist )
|
|
status = persist(aCategoryName, aEntryName, aValue);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
nsCategoryManager::DeleteCategoryEntry( const char *aCategoryName,
|
|
const char *aEntryName,
|
|
PRBool aDontPersist,
|
|
char **_retval )
|
|
{
|
|
|
|
NS_ASSERTION(aCategoryName, "aCategoryName is NULL!");
|
|
NS_ASSERTION(aEntryName, "aEntryName is NULL!");
|
|
NS_ASSERTION(_retval, "_retval is NULL!");
|
|
|
|
|
|
/*
|
|
Note: no errors are reported since failure to delete
|
|
probably won't hurt you, and returning errors seriously
|
|
inconveniences JS clients
|
|
*/
|
|
|
|
CategoryNode* category = find_category(aCategoryName);
|
|
if (category)
|
|
{
|
|
nsStringKey entryKey(aEntryName);
|
|
category->RemoveAndDelete(&entryKey);
|
|
}
|
|
|
|
nsresult status = NS_OK;
|
|
if ( aDontPersist )
|
|
status = dont_persist(aCategoryName, aEntryName);
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
nsCategoryManager::DeleteCategory( const char *aCategoryName )
|
|
{
|
|
NS_ASSERTION(aCategoryName, "aCategoryName is NULL!");
|
|
|
|
// QUESTION: consider whether this should be an error
|
|
nsStringKey categoryKey(aCategoryName);
|
|
return RemoveAndDelete(&categoryKey) ? NS_OK : NS_ERROR_NOT_AVAILABLE;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
nsCategoryManager::EnumerateCategory( const char *aCategoryName,
|
|
nsISimpleEnumerator **_retval )
|
|
{
|
|
NS_ASSERTION(aCategoryName, "aCategoryName is NULL!");
|
|
NS_ASSERTION(_retval, "_retval is NULL!");
|
|
|
|
*_retval = 0;
|
|
|
|
nsresult status = NS_ERROR_NOT_AVAILABLE;
|
|
CategoryNode* category = find_category(aCategoryName);
|
|
if (category)
|
|
{
|
|
nsCOMPtr<nsIEnumerator> innerEnumerator;
|
|
if ( NS_SUCCEEDED(status = NS_NewHashtableEnumerator(category, ExtractKeyString, 0, getter_AddRefs(innerEnumerator))) )
|
|
status = NS_NewAdapterEnumerator(_retval, innerEnumerator);
|
|
}
|
|
|
|
// If you couldn't find the category, or had trouble creating an enumerator...
|
|
if ( !NS_SUCCEEDED(status) )
|
|
{
|
|
NS_IF_RELEASE(*_retval);
|
|
status = NS_NewEmptyEnumerator(_retval);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
nsCategoryManager::GetCategoryContents( const char *category,
|
|
char ***entries,
|
|
char ***values,
|
|
PRInt32 *count )
|
|
{
|
|
// BULLSHIT ALERT: Wasn't implemented in JS either.
|
|
// Will people use this? If not, let's get rid of it
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
nsCategoryManager::RegisterCategoryHandler( const char* /* aCategoryName */,
|
|
nsICategoryHandler* /* aHandler */,
|
|
PRInt32 /* aMode */,
|
|
nsICategoryHandler** /* _retval */ )
|
|
{
|
|
// BULLSHIT ALERT: Category `handler's currently not implemented
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
nsCategoryManager::UnregisterCategoryHandler( const char *category,
|
|
nsICategoryHandler *handler,
|
|
nsICategoryHandler *previous )
|
|
{
|
|
// BULLSHIT ALERT: Category `handler's currently not implemented.
|
|
// Wasn't implemented in the JS version either.
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
|
|
class nsCategoryManagerFactory : public nsIFactory
|
|
{
|
|
public:
|
|
nsCategoryManagerFactory();
|
|
|
|
NS_DECL_ISUPPORTS
|
|
NS_DECL_NSIFACTORY
|
|
};
|
|
|
|
NS_IMPL_ISUPPORTS1(nsCategoryManagerFactory, nsIFactory)
|
|
|
|
nsCategoryManagerFactory::nsCategoryManagerFactory()
|
|
{
|
|
NS_INIT_REFCNT();
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsCategoryManagerFactory::CreateInstance( nsISupports* aOuter, const nsIID& aIID, void** aResult )
|
|
{
|
|
// assert(aResult);
|
|
|
|
*aResult = 0;
|
|
|
|
nsresult status = NS_OK;
|
|
if ( aOuter )
|
|
status = NS_ERROR_NO_AGGREGATION;
|
|
else
|
|
{
|
|
nsCategoryManager* raw_category_manager;
|
|
nsCOMPtr<nsICategoryManager> new_category_manager = (raw_category_manager = new nsCategoryManager);
|
|
if ( new_category_manager )
|
|
{
|
|
if ( NS_SUCCEEDED(status = raw_category_manager->initialize()) )
|
|
status = new_category_manager->QueryInterface(aIID, aResult);
|
|
}
|
|
else
|
|
status = NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsCategoryManagerFactory::LockFactory( PRBool )
|
|
{
|
|
// Not implemented...
|
|
return NS_OK;
|
|
}
|
|
|
|
extern "C"
|
|
NS_EXPORT
|
|
nsresult
|
|
NS_CategoryManagerGetFactory( nsIFactory** aFactory )
|
|
{
|
|
// assert(aFactory);
|
|
|
|
nsresult status;
|
|
|
|
*aFactory = 0;
|
|
nsIFactory* new_factory = NS_STATIC_CAST(nsIFactory*, new nsCategoryManagerFactory);
|
|
if (new_factory)
|
|
{
|
|
*aFactory = new_factory;
|
|
NS_ADDREF(*aFactory);
|
|
status = NS_OK;
|
|
}
|
|
else
|
|
status = NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
return status;
|
|
}
|