RetroZilla/extensions/spellcheck/src/mozPersonalDictionary.cpp
2015-10-20 23:03:22 -04:00

289 lines
9.3 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla 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/MPL/
*
* 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 Spellchecker Component.
*
* The Initial Developer of the Original Code is
* David Einstein.
* Portions created by the Initial Developer are Copyright (C) 2001
* the Initial Developer. All Rights Reserved.
*
* Contributor(s): David Einstein <Deinst@world.std.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "mozPersonalDictionary.h"
#include "nsIUnicharInputStream.h"
#include "nsReadableUtils.h"
#include "nsIFile.h"
#include "nsAppDirectoryServiceDefs.h"
#include "nsICharsetConverterManager.h"
#include "nsICharsetAlias.h"
#include "nsIObserverService.h"
#include "nsIPrefService.h"
#include "nsIPrefBranch.h"
#include "nsIPrefBranch2.h"
#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);
const int kMaxWordLen=256;
/**
* This is the most braindead implementation of a personal dictionary possible.
* There is not much complexity needed, though. It could be made much faster,
* and probably should, but I don't see much need for more in terms of interface.
*
* Allowing personal words to be associated with only certain dictionaries maybe.
*
* TODO:
* Implement the suggestion record.
*/
NS_IMPL_ISUPPORTS3(mozPersonalDictionary, mozIPersonalDictionary, nsIObserver, nsISupportsWeakReference)
mozPersonalDictionary::mozPersonalDictionary()
: mDirty(PR_FALSE)
{
}
mozPersonalDictionary::~mozPersonalDictionary()
{
}
nsresult mozPersonalDictionary::Init()
{
if (!mDictionaryTable.Init() || !mIgnoreTable.Init())
return NS_ERROR_OUT_OF_MEMORY;
nsresult rv;
nsCOMPtr<nsIObserverService> svc =
do_GetService("@mozilla.org/observer-service;1", &rv);
if (NS_SUCCEEDED(rv) && svc)
rv = svc->AddObserver(this, "profile-do-change", PR_TRUE); // we want to reload the dictionary if the profile switches
if (NS_FAILED(rv)) return rv;
Load();
return NS_OK;
}
/* void Load (); */
NS_IMETHODIMP mozPersonalDictionary::Load()
{
//FIXME Deinst -- get dictionary name from prefs;
nsresult res;
nsCOMPtr<nsIFile> theFile;
PRBool dictExists;
res = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(theFile));
if(NS_FAILED(res)) return res;
if(!theFile)return NS_ERROR_FAILURE;
res = theFile->Append(NS_LITERAL_STRING(MOZ_PERSONAL_DICT_NAME));
if(NS_FAILED(res)) return res;
res = theFile->Exists(&dictExists);
if(NS_FAILED(res)) return res;
if (!dictExists) {
// Nothing is really wrong...
return NS_OK;
}
nsCOMPtr<nsIInputStream> inStream;
NS_NewLocalFileInputStream(getter_AddRefs(inStream), theFile);
nsCOMPtr<nsIUnicharInputStream> convStream;
res = NS_NewUTF8ConverterStream(getter_AddRefs(convStream), inStream, 0);
if(NS_FAILED(res)) return res;
// we're rereading to get rid of the old data -- we shouldn't have any, but...
mDictionaryTable.Clear();
PRUnichar c;
PRUint32 nRead;
PRBool done = PR_FALSE;
do{ // read each line of text into the string array.
if( (NS_OK != convStream->Read(&c, 1, &nRead)) || (nRead != 1)) break;
while(!done && ((c == '\n') || (c == '\r'))){
if( (NS_OK != convStream->Read(&c, 1, &nRead)) || (nRead != 1)) done = PR_TRUE;
}
if (!done){
nsAutoString word;
while((c != '\n') && (c != '\r') && !done){
word.Append(c);
if( (NS_OK != convStream->Read(&c, 1, &nRead)) || (nRead != 1)) done = PR_TRUE;
}
mDictionaryTable.PutEntry(word.get());
}
} 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()
{
nsCOMPtr<nsIFile> theFile;
nsresult res;
if(!mDirty) return NS_OK;
//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;
res = theFile->Append(NS_LITERAL_STRING(MOZ_PERSONAL_DICT_NAME));
if(NS_FAILED(res)) return res;
nsCOMPtr<nsIOutputStream> outStream;
NS_NewLocalFileOutputStream(getter_AddRefs(outStream), theFile, PR_CREATE_FILE | PR_WRONLY | PR_TRUNCATE ,0664);
// get a buffered output stream 4096 bytes big, to optimize writes
nsCOMPtr<nsIOutputStream> bufferedOutputStream;
res = NS_NewBufferedOutputStream(getter_AddRefs(bufferedOutputStream), outStream, 4096);
if (NS_FAILED(res)) return res;
nsStringArray array(mDictionaryTable.Count());
mDictionaryTable.EnumerateEntries(AddHostToStringArray, &array);
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;
}
/* readonly attribute nsIStringEnumerator GetWordList() */
NS_IMETHODIMP mozPersonalDictionary::GetWordList(nsIStringEnumerator **aWords)
{
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 *aWord, const PRUnichar *aLang)
{
mDictionaryTable.PutEntry(aWord);
mDirty = PR_TRUE;
return NS_OK;
}
/* void RemoveWord (in wstring word); */
NS_IMETHODIMP mozPersonalDictionary::RemoveWord(const PRUnichar *aWord, const PRUnichar *aLang)
{
mDictionaryTable.RemoveEntry(aWord);
mDirty = PR_TRUE;
return NS_OK;
}
/* void IgnoreWord (in wstring word); */
NS_IMETHODIMP mozPersonalDictionary::IgnoreWord(const PRUnichar *aWord)
{
// avoid adding duplicate words to the ignore list
if (aWord && !mIgnoreTable.GetEntry(aWord))
mIgnoreTable.PutEntry(aWord);
return NS_OK;
}
/* void EndSession (); */
NS_IMETHODIMP mozPersonalDictionary::EndSession()
{
Save(); // save any custom words at the end of a spell check session
mIgnoreTable.Clear();
return NS_OK;
}
/* void AddCorrection (in wstring word, in wstring correction); */
NS_IMETHODIMP mozPersonalDictionary::AddCorrection(const PRUnichar *word, const PRUnichar *correction, const PRUnichar *lang)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
/* void RemoveCorrection (in wstring word, in wstring correction); */
NS_IMETHODIMP mozPersonalDictionary::RemoveCorrection(const PRUnichar *word, const PRUnichar *correction, const PRUnichar *lang)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
/* void GetCorrection (in wstring word, [array, size_is (count)] out wstring words, out PRUint32 count); */
NS_IMETHODIMP mozPersonalDictionary::GetCorrection(const PRUnichar *word, PRUnichar ***words, PRUint32 *count)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
/* void observe (in nsISupports aSubject, in string aTopic, in wstring aData); */
NS_IMETHODIMP mozPersonalDictionary::Observe(nsISupports *aSubject, const char *aTopic, const PRUnichar *aData)
{
if (!nsCRT::strcmp(aTopic, "profile-do-change")) {
Load(); // load automatically clears out the existing dictionary table
}
return NS_OK;
}