diff --git a/mozilla/extensions/spellcheck/idl/mozIPersonalDictionary.idl b/mozilla/extensions/spellcheck/idl/mozIPersonalDictionary.idl index 012ba9b50df..f4829a51494 100644 --- a/mozilla/extensions/spellcheck/idl/mozIPersonalDictionary.idl +++ b/mozilla/extensions/spellcheck/idl/mozIPersonalDictionary.idl @@ -38,6 +38,8 @@ #include "nsISupports.idl" +interface nsIStringEnumerator; + [scriptable, uuid(7EF52EAF-B7E1-462B-87E2-5D1DBACA9048)] /** @@ -46,11 +48,6 @@ interface mozIPersonalDictionary : nsISupports { - /** - * Initialize the dictionary - */ - void init(); - /** * Load the dictionary */ @@ -62,9 +59,9 @@ interface mozIPersonalDictionary : nsISupports { void save(); /** - * Get the list of words + * Get the (lexicographically sorted) list of words */ - void getWordList([array, size_is(count)] out wstring words, out PRUint32 count); + readonly attribute nsIStringEnumerator wordList; /** * Check a unicode string diff --git a/mozilla/extensions/spellcheck/src/Makefile.in b/mozilla/extensions/spellcheck/src/Makefile.in index dbf2f7b8f3e..a06ad1e0bf3 100644 --- a/mozilla/extensions/spellcheck/src/Makefile.in +++ b/mozilla/extensions/spellcheck/src/Makefile.in @@ -74,7 +74,6 @@ CPPSRCS = \ mozEnglishWordUtils.cpp \ mozGenericWordUtils.cpp \ mozSpellI18NManager.cpp \ - nsAVLTree.cpp \ $(NULL) EXTRA_DSO_LDOPTS = \ diff --git a/mozilla/extensions/spellcheck/src/mozPersonalDictionary.cpp b/mozilla/extensions/spellcheck/src/mozPersonalDictionary.cpp index c1e7da563cf..f9a67076734 100644 --- a/mozilla/extensions/spellcheck/src/mozPersonalDictionary.cpp +++ b/mozilla/extensions/spellcheck/src/mozPersonalDictionary.cpp @@ -42,7 +42,6 @@ #include "nsAppDirectoryServiceDefs.h" #include "nsICharsetConverterManager.h" #include "nsICharsetAlias.h" -#include "nsAVLTree.h" #include "nsIObserverService.h" #include "nsIPrefService.h" #include "nsIPrefBranch.h" @@ -50,6 +49,7 @@ #include "nsIWeakReference.h" #include "nsCRT.h" #include "nsNetUtil.h" +#include "nsStringEnumerator.h" #define MOZ_PERSONAL_DICT_NAME "persdict.dat" static NS_DEFINE_CID(kCharsetConverterManagerCID, NS_ICHARSETCONVERTERMANAGER_CID); @@ -72,108 +72,21 @@ static PRBool SessionSave=PR_FALSE; NS_IMPL_ISUPPORTS3(mozPersonalDictionary, mozIPersonalDictionary, nsIObserver, nsISupportsWeakReference) -/* AVL node functors */ - -/* - * String comparitor for Unicode tree - **/ -class StringNodeComparitor: public nsAVLNodeComparitor { -public: - StringNodeComparitor(){} - virtual ~StringNodeComparitor(){} - virtual PRInt32 operator() (void *item1,void *item2){ - return nsCRT::strcmp((PRUnichar *)item1,(PRUnichar *)item2); - } -}; - -/* - * the generic deallocator - **/ -class DeallocatorFunctor: public nsAVLNodeFunctor { -public: - DeallocatorFunctor(){} - virtual ~DeallocatorFunctor(){} - virtual void* operator() (void * anItem){ - nsMemory::Free(anItem); - return nsnull; - } -}; - -static StringNodeComparitor *gStringNodeComparitor; -static DeallocatorFunctor *gDeallocatorFunctor; - -/* - * Copy the tree to a waiting array of Unichar pointers. - * All the strings are newly created. It is the callers responsibility to free them - **/ -class CopyToArrayFunctor: public nsAVLNodeFunctor { -public: - CopyToArrayFunctor(PRUnichar **tabulaRasa){ - data = tabulaRasa; - count =0; - res = NS_OK; - } - virtual ~CopyToArrayFunctor(){} - nsresult GetResult(){return res;} - virtual void* operator() (void * anItem){ - PRUnichar * word=(PRUnichar *) anItem; - if(NS_SUCCEEDED(res)){ - data[count]=ToNewUnicode(nsDependentString(word)); - if(!data[count]) res = NS_ERROR_OUT_OF_MEMORY; - return (void*) (data[count++]); - } - return nsnull; - } -protected: - nsresult res; - PRUnichar **data; - PRUint32 count; -}; - -/* - * Copy the tree to an open file. - **/ -class CopyToStreamFunctor: public nsAVLNodeFunctor { -public: - CopyToStreamFunctor(nsIOutputStream *aStream):mStream(aStream){ - res = NS_OK; - } - virtual ~CopyToStreamFunctor(){} - nsresult GetResult(){return res;} - virtual void* operator() (void * anItem){ - nsString word((PRUnichar *) anItem); - if(NS_SUCCEEDED(res)){ - PRUint32 bytesWritten; - word.Append(NS_LITERAL_STRING("\n")); - NS_ConvertUCS2toUTF8 UTF8word(word); - res = mStream->Write(UTF8word.get(),UTF8word.Length(),&bytesWritten); - } - return nsnull; - } -protected: - nsresult res; - nsIOutputStream* mStream; -}; - -mozPersonalDictionary::mozPersonalDictionary():mUnicodeTree(nsnull),mUnicodeIgnoreTree(nsnull) +mozPersonalDictionary::mozPersonalDictionary() + : mDirty(PR_FALSE) { - NS_ASSERTION(!gStringNodeComparitor,"Someone's been writing in my statics! I'm a Singleton Bear!"); - if(!gStringNodeComparitor){ - gStringNodeComparitor = new StringNodeComparitor; - gDeallocatorFunctor = new DeallocatorFunctor; - } } mozPersonalDictionary::~mozPersonalDictionary() { - delete mUnicodeTree; - delete mUnicodeIgnoreTree; } -NS_IMETHODIMP mozPersonalDictionary::Init() +nsresult mozPersonalDictionary::Init() { - nsresult rv; + if (!mDictionaryTable.Init() || !mIgnoreTable.Init()) + return NS_ERROR_OUT_OF_MEMORY; + nsresult rv; nsCOMPtr svc = do_GetService("@mozilla.org/observer-service;1", &rv); if (NS_SUCCEEDED(rv) && svc) { @@ -183,13 +96,13 @@ NS_IMETHODIMP mozPersonalDictionary::Init() if (NS_SUCCEEDED(rv)) rv = svc->AddObserver(this, "profile-before-change", PR_TRUE); if (NS_SUCCEEDED(rv)) - rv = svc->AddObserver(this, "profile-after-change", PR_TRUE); + rv = svc->AddObserver(this, "profile-do-change", PR_TRUE); } - if(NS_FAILED(rv)) return rv; + if (NS_FAILED(rv)) return rv; nsCOMPtr prefs(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv)); - if(NS_SUCCEEDED(rv)) { - if(NS_FAILED(prefs->GetBoolPref(spellchecker_savePref, &SessionSave))){ + if (NS_SUCCEEDED(rv)) { + if (NS_FAILED(prefs->GetBoolPref(spellchecker_savePref, &SessionSave))){ SessionSave = PR_TRUE; } prefs->AddObserver(spellchecker_savePref, this, PR_TRUE); @@ -198,9 +111,11 @@ NS_IMETHODIMP mozPersonalDictionary::Init() SessionSave = PR_FALSE; } - if(NS_FAILED(rv)) return rv; + if (NS_FAILED(rv)) return rv; - return Load(); + Load(); + + return NS_OK; } /* void Load (); */ @@ -219,14 +134,9 @@ NS_IMETHODIMP mozPersonalDictionary::Load() res = theFile->Exists(&dictExists); if(NS_FAILED(res)) return res; - if(!dictExists) { - //create new user dictionary - nsCOMPtr outStream; - NS_NewLocalFileOutputStream(getter_AddRefs(outStream), theFile, PR_CREATE_FILE | PR_WRONLY | PR_TRUNCATE ,0664); - - CopyToStreamFunctor writer(outStream); - if(NS_FAILED(res)) return res; - if(!outStream)return NS_ERROR_FAILURE; + if (!dictExists) { + // Nothing is really wrong... + return NS_OK; } nsCOMPtr inStream; @@ -236,8 +146,7 @@ NS_IMETHODIMP mozPersonalDictionary::Load() if(NS_FAILED(res)) return res; // we're rereading to get rid of the old data -- we shouldn't have any, but... - delete mUnicodeTree; - mUnicodeTree = new nsAVLTree(*gStringNodeComparitor,gDeallocatorFunctor); + mDictionaryTable.Clear(); PRUnichar c; PRUint32 nRead; @@ -253,14 +162,24 @@ NS_IMETHODIMP mozPersonalDictionary::Load() word.Append(c); if( (NS_OK != convStream->Read(&c, 1, &nRead)) || (nRead != 1)) done = PR_TRUE; } - mUnicodeTree->AddItem((void *)ToNewUnicode(word)); + mDictionaryTable.PutEntry(word.get()); } - }while(!done); + } while(!done); mDirty = PR_FALSE; return res; } +// A little helper function to add the key to the list. +// This is not threadsafe, and only safe if the consumer does not +// modify the list. +PR_STATIC_CALLBACK(PLDHashOperator) +AddHostToStringArray(nsUniCharEntry *aEntry, void *aArg) +{ + NS_STATIC_CAST(nsStringArray*, aArg)->AppendString(nsDependentString(aEntry->GetKey())); + return PL_DHASH_NEXT; +} + /* void Save (); */ NS_IMETHODIMP mozPersonalDictionary::Save() { @@ -268,8 +187,8 @@ NS_IMETHODIMP mozPersonalDictionary::Save() nsresult res; if(!mDirty) return NS_OK; - //FIXME Deinst -- get dictionary name from prefs; + //FIXME Deinst -- get dictionary name from prefs; res = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(theFile)); if(NS_FAILED(res)) return res; if(!theFile)return NS_ERROR_FAILURE; @@ -279,97 +198,83 @@ NS_IMETHODIMP mozPersonalDictionary::Save() nsCOMPtr outStream; NS_NewLocalFileOutputStream(getter_AddRefs(outStream), theFile, PR_CREATE_FILE | PR_WRONLY | PR_TRUNCATE ,0664); - CopyToStreamFunctor writer(outStream); - if(NS_FAILED(res)) return res; - if(!outStream)return NS_ERROR_FAILURE; - if (mUnicodeTree) mUnicodeTree->ForEach(writer); - mDirty = PR_FALSE; - return NS_OK; -} + // get a buffered output stream 4096 bytes big, to optimize writes + nsCOMPtr bufferedOutputStream; + res = NS_NewBufferedOutputStream(getter_AddRefs(bufferedOutputStream), outStream, 4096); + if (NS_FAILED(res)) return res; -/* void GetWordList ([array, size_is (count)] out wstring words, out PRUint32 count); */ -NS_IMETHODIMP mozPersonalDictionary::GetWordList(PRUnichar ***words, PRUint32 *count) -{ - if(!words || !count) - return NS_ERROR_NULL_POINTER; - *words=0; - *count=0; - PRUnichar **tmpPtr = 0; - if(!mUnicodeTree){ - return NS_OK; - } - tmpPtr = (PRUnichar **)nsMemory::Alloc(sizeof(PRUnichar *) * (mUnicodeTree->GetCount())); - if (!tmpPtr) - return NS_ERROR_OUT_OF_MEMORY; - CopyToArrayFunctor pitneyBowes(tmpPtr); - mUnicodeTree->ForEach(pitneyBowes); + nsStringArray array(mDictionaryTable.Count()); + mDictionaryTable.EnumerateEntries(AddHostToStringArray, &array); - nsresult res = pitneyBowes.GetResult(); - if(NS_SUCCEEDED(res)){ - *count = mUnicodeTree->GetCount(); - *words = tmpPtr; + PRUint32 bytesWritten; + nsCAutoString utf8Key; + for (PRInt32 i = 0; i < array.Count(); ++i ) { + const nsString *key = array[i]; + CopyUTF16toUTF8(*key, utf8Key); + + bufferedOutputStream->Write(utf8Key.get(), utf8Key.Length(), &bytesWritten); + bufferedOutputStream->Write("\n", 1, &bytesWritten); } return res; } -/* boolean Check (in wstring word, in wstring language); */ -NS_IMETHODIMP mozPersonalDictionary::Check(const PRUnichar *word, const PRUnichar *aLanguage, PRBool *_retval) +/* readonly attribute nsIStringEnumerator GetWordList() */ +NS_IMETHODIMP mozPersonalDictionary::GetWordList(nsIStringEnumerator **aWords) { - if(!word || !_retval || !mUnicodeTree) - return NS_ERROR_NULL_POINTER; - if(mUnicodeTree->FindItem((void *)word)){ - *_retval = PR_TRUE; - } - else{ - if(mUnicodeIgnoreTree&&mUnicodeIgnoreTree->FindItem((void *)word)){ - *_retval = PR_TRUE; - } - else{ - *_retval = PR_FALSE; - } - } + NS_ENSURE_ARG_POINTER(aWords); + *aWords = nsnull; + + nsStringArray *array = new nsStringArray(mDictionaryTable.Count()); + if (!array) + return NS_ERROR_OUT_OF_MEMORY; + + mDictionaryTable.EnumerateEntries(AddHostToStringArray, array); + + array->Sort(); + + return NS_NewAdoptingStringEnumerator(aWords, array); +} + +/* boolean Check (in wstring word, in wstring language); */ +NS_IMETHODIMP mozPersonalDictionary::Check(const PRUnichar *aWord, const PRUnichar *aLanguage, PRBool *aResult) +{ + NS_ENSURE_ARG_POINTER(aWord); + NS_ENSURE_ARG_POINTER(aResult); + + *aResult = (mDictionaryTable.GetEntry(aWord) || mIgnoreTable.GetEntry(aWord)); return NS_OK; } /* void AddWord (in wstring word); */ -NS_IMETHODIMP mozPersonalDictionary::AddWord(const PRUnichar *word, const PRUnichar *lang) +NS_IMETHODIMP mozPersonalDictionary::AddWord(const PRUnichar *aWord, const PRUnichar *aLang) { - nsAutoString temp(word); - if(mUnicodeTree) mUnicodeTree->AddItem(ToNewUnicode(nsDependentString(word))); - mDirty=PR_TRUE; - + mDictionaryTable.PutEntry(aWord); + mDirty = PR_TRUE; return NS_OK; } /* void RemoveWord (in wstring word); */ -NS_IMETHODIMP mozPersonalDictionary::RemoveWord(const PRUnichar *word, const PRUnichar *lang) +NS_IMETHODIMP mozPersonalDictionary::RemoveWord(const PRUnichar *aWord, const PRUnichar *aLang) { - nsAutoString temp(word); - if(mUnicodeTree) mUnicodeTree->RemoveItem((void *)word); - mDirty=PR_TRUE; - + mDictionaryTable.RemoveEntry(aWord); + mDirty = PR_TRUE; return NS_OK; } + /* void IgnoreWord (in wstring word); */ -NS_IMETHODIMP mozPersonalDictionary::IgnoreWord(const PRUnichar *word) +NS_IMETHODIMP mozPersonalDictionary::IgnoreWord(const PRUnichar *aWord) { - if(!mUnicodeIgnoreTree){ - mUnicodeIgnoreTree=new nsAVLTree(*gStringNodeComparitor,gDeallocatorFunctor); - } - if(!mUnicodeIgnoreTree){ - return NS_ERROR_OUT_OF_MEMORY; - } - mUnicodeIgnoreTree->AddItem(ToNewUnicode(nsDependentString(word))); - + mIgnoreTable.PutEntry(aWord); return NS_OK; } /* void EndSession (); */ NS_IMETHODIMP mozPersonalDictionary::EndSession() { - if(SessionSave)Save(); - delete mUnicodeIgnoreTree; - mUnicodeIgnoreTree=nsnull; + if (SessionSave) + Save(); + + mIgnoreTable.Clear(); return NS_OK; } @@ -396,17 +301,15 @@ NS_IMETHODIMP mozPersonalDictionary::Observe(nsISupports *aSubject, const char * { if (!nsCRT::strcmp(aTopic, "profile-before-change") || !nsCRT::strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) { Save(); - delete mUnicodeTree; - delete mUnicodeIgnoreTree; - mUnicodeTree=nsnull; - mUnicodeIgnoreTree=nsnull; + mDictionaryTable.Clear(); + mIgnoreTable.Clear(); } else if (!nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) { nsCOMPtr prefs(do_QueryInterface(aSubject)); if(prefs) prefs->GetBoolPref(spellchecker_savePref, &SessionSave); } - if (!nsCRT::strcmp(aTopic, "profile-before-change")) { + if (!nsCRT::strcmp(aTopic, "profile-do-change")) { Load(); } diff --git a/mozilla/extensions/spellcheck/src/mozPersonalDictionary.h b/mozilla/extensions/spellcheck/src/mozPersonalDictionary.h index 983b0849d3b..5e06378e4ca 100644 --- a/mozilla/extensions/spellcheck/src/mozPersonalDictionary.h +++ b/mozilla/extensions/spellcheck/src/mozPersonalDictionary.h @@ -45,8 +45,8 @@ #include "nsIUnicodeEncoder.h" #include "nsIObserver.h" #include "nsWeakReference.h" - -class nsAVLTree; +#include "nsTHashtable.h" +#include "nsCRT.h" #define MOZ_PERSONALDICTIONARY_CONTRACTID "@mozilla.org/spellchecker/personaldictionary;1" #define MOZ_PERSONALDICTIONARY_CID \ @@ -54,6 +54,39 @@ class nsAVLTree; 0X7EF52EAF, 0XB7E1, 0X462B, \ { 0X87, 0XE2, 0X5D, 0X1D, 0XBA, 0XCA, 0X90, 0X48 } } +class nsUniCharEntry : public PLDHashEntryHdr +{ +public: + // Hash methods + typedef const PRUnichar* KeyType; + typedef const PRUnichar* KeyTypePointer; + + nsUniCharEntry(const PRUnichar* aKey) : mKey(nsCRT::strdup(aKey)) {} + nsUniCharEntry(const nsUniCharEntry& toCopy) + { + NS_NOTREACHED("ALLOW_MEMMOVE is set, so copy ctor shouldn't be called"); + } + + ~nsUniCharEntry() + { + if (mKey) + nsCRT::free(mKey); + } + + KeyType GetKey() const { return mKey; } + KeyTypePointer GetKeyPointer() const { return mKey; } + PRBool KeyEquals(KeyTypePointer aKey) const { return !nsCRT::strcmp(mKey, aKey); } + static KeyTypePointer KeyToPointer(KeyType aKey) { return aKey; } + + static PLDHashNumber HashKey(KeyTypePointer aKey) { return nsCRT::HashCode(aKey); } + + enum { ALLOW_MEMMOVE = PR_TRUE }; + +private: + PRUnichar *mKey; +}; + + class mozPersonalDictionary : public mozIPersonalDictionary, public nsIObserver, public nsSupportsWeakReference @@ -66,12 +99,14 @@ public: mozPersonalDictionary(); virtual ~mozPersonalDictionary(); + nsresult Init(); + protected: nsStringArray mDictionary; /* use something a little smarter eventually*/ PRBool mDirty; /* has the dictionary been modified */ - nsAVLTree *mUnicodeTree; /* the dictionary entries */ - nsAVLTree *mUnicodeIgnoreTree; /* the ignore all entries */ - nsCOMPtr mEncoder; /*Encoder to use to compare with spellchecker word */ + nsTHashtable mDictionaryTable; + nsTHashtable mIgnoreTable; + nsCOMPtr mEncoder; /*Encoder to use to compare with spellchecker word */ }; #endif diff --git a/mozilla/extensions/spellcheck/src/mozSpellChecker.cpp b/mozilla/extensions/spellcheck/src/mozSpellChecker.cpp index 62de8346b3b..9e66b601cd4 100644 --- a/mozilla/extensions/spellcheck/src/mozSpellChecker.cpp +++ b/mozilla/extensions/spellcheck/src/mozSpellChecker.cpp @@ -39,13 +39,12 @@ #include "mozSpellChecker.h" #include "nsIServiceManager.h" #include "mozISpellI18NManager.h" +#include "nsIStringEnumerator.h" NS_IMPL_ISUPPORTS1(mozSpellChecker, nsISpellChecker) mozSpellChecker::mozSpellChecker() { - mDictionaryName.SetLength(0); - nsresult rv; mPersonalDictionary = do_GetService("@mozilla.org/spellchecker/personaldictionary;1",&rv); @@ -56,7 +55,6 @@ mozSpellChecker::mozSpellChecker() if (NS_FAILED(rv)) { NS_ERROR("Could not get spell checker"); } - mPersonalDictionary->Init(); mSpellCheckingEngine->SetPersonalDictionary(mPersonalDictionary); } @@ -255,19 +253,18 @@ mozSpellChecker::RemoveWordFromPersonalDictionary(const nsAString &aWord) NS_IMETHODIMP mozSpellChecker::GetPersonalDictionary(nsStringArray *aWordList) { - nsAutoString temp; - PRUint32 count,i; - PRUnichar **words; - if(!aWordList || !mPersonalDictionary) return NS_ERROR_NULL_POINTER; - mPersonalDictionary->GetWordList(&words,&count); - for(i=0;iAppendString(temp); - } - NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(count, words); + nsCOMPtr words; + mPersonalDictionary->GetWordList(getter_AddRefs(words)); + + PRBool hasMore; + nsAutoString word; + while (NS_SUCCEEDED(words->HasMore(&hasMore)) && hasMore) { + words->GetNext(word); + aWordList->AppendString(word); + } return NS_OK; } diff --git a/mozilla/extensions/spellcheck/src/mozSpellCheckerFactory.cpp b/mozilla/extensions/spellcheck/src/mozSpellCheckerFactory.cpp index 8b2772c62e4..79ea16c5041 100644 --- a/mozilla/extensions/spellcheck/src/mozSpellCheckerFactory.cpp +++ b/mozilla/extensions/spellcheck/src/mozSpellCheckerFactory.cpp @@ -55,7 +55,7 @@ // NS_GENERIC_FACTORY_CONSTRUCTOR(mozSpellChecker) -NS_GENERIC_FACTORY_CONSTRUCTOR(mozPersonalDictionary) +NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(mozPersonalDictionary, Init) NS_GENERIC_FACTORY_CONSTRUCTOR(mozSpellI18NManager) ////////////////////////////////////////////////////////////////////////