extensions/spellcheck/src/mozPersonalDictionary.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/extensions/spellcheck/src/mozPersonalDictionary.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,274 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +#include "mozPersonalDictionary.h"
    1.10 +#include "nsIUnicharInputStream.h"
    1.11 +#include "nsReadableUtils.h"
    1.12 +#include "nsIFile.h"
    1.13 +#include "nsAppDirectoryServiceDefs.h"
    1.14 +#include "nsICharsetConverterManager.h"
    1.15 +#include "nsIObserverService.h"
    1.16 +#include "nsIPrefService.h"
    1.17 +#include "nsIPrefBranch.h"
    1.18 +#include "nsIWeakReference.h"
    1.19 +#include "nsCRT.h"
    1.20 +#include "nsNetUtil.h"
    1.21 +#include "nsStringEnumerator.h"
    1.22 +#include "nsUnicharInputStream.h"
    1.23 +
    1.24 +#define MOZ_PERSONAL_DICT_NAME "persdict.dat"
    1.25 +
    1.26 +const int kMaxWordLen=256;
    1.27 +
    1.28 +/**
    1.29 + * This is the most braindead implementation of a personal dictionary possible.
    1.30 + * There is not much complexity needed, though.  It could be made much faster,
    1.31 + *  and probably should, but I don't see much need for more in terms of interface.
    1.32 + *
    1.33 + * Allowing personal words to be associated with only certain dictionaries maybe.
    1.34 + *
    1.35 + * TODO:
    1.36 + * Implement the suggestion record.
    1.37 + */
    1.38 +
    1.39 +
    1.40 +NS_IMPL_CYCLE_COLLECTING_ADDREF(mozPersonalDictionary)
    1.41 +NS_IMPL_CYCLE_COLLECTING_RELEASE(mozPersonalDictionary)
    1.42 +
    1.43 +NS_INTERFACE_MAP_BEGIN(mozPersonalDictionary)
    1.44 +  NS_INTERFACE_MAP_ENTRY(mozIPersonalDictionary)
    1.45 +  NS_INTERFACE_MAP_ENTRY(nsIObserver)
    1.46 +  NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
    1.47 +  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, mozIPersonalDictionary)
    1.48 +  NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(mozPersonalDictionary)
    1.49 +NS_INTERFACE_MAP_END
    1.50 +
    1.51 +NS_IMPL_CYCLE_COLLECTION(mozPersonalDictionary, mEncoder)
    1.52 +
    1.53 +mozPersonalDictionary::mozPersonalDictionary()
    1.54 + : mDirty(false)
    1.55 +{
    1.56 +}
    1.57 +
    1.58 +mozPersonalDictionary::~mozPersonalDictionary()
    1.59 +{
    1.60 +}
    1.61 +
    1.62 +nsresult mozPersonalDictionary::Init()
    1.63 +{
    1.64 +  nsCOMPtr<nsIObserverService> svc =
    1.65 +    do_GetService("@mozilla.org/observer-service;1");
    1.66 +
    1.67 +  NS_ENSURE_STATE(svc);
    1.68 +  // we want to reload the dictionary if the profile switches
    1.69 +  nsresult rv = svc->AddObserver(this, "profile-do-change", true);
    1.70 +  NS_ENSURE_SUCCESS(rv, rv);
    1.71 +  rv = svc->AddObserver(this, "profile-before-change", true);
    1.72 +  NS_ENSURE_SUCCESS(rv, rv);
    1.73 +
    1.74 +  Load();
    1.75 +  
    1.76 +  return NS_OK;
    1.77 +}
    1.78 +
    1.79 +/* void Load (); */
    1.80 +NS_IMETHODIMP mozPersonalDictionary::Load()
    1.81 +{
    1.82 +  //FIXME Deinst  -- get dictionary name from prefs;
    1.83 +  nsresult res;
    1.84 +  nsCOMPtr<nsIFile> theFile;
    1.85 +  bool dictExists;
    1.86 +
    1.87 +  res = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(theFile));
    1.88 +  if(NS_FAILED(res)) return res;
    1.89 +  if(!theFile)return NS_ERROR_FAILURE;
    1.90 +  res = theFile->Append(NS_LITERAL_STRING(MOZ_PERSONAL_DICT_NAME));
    1.91 +  if(NS_FAILED(res)) return res;
    1.92 +  res = theFile->Exists(&dictExists);
    1.93 +  if(NS_FAILED(res)) return res;
    1.94 +
    1.95 +  if (!dictExists) {
    1.96 +    // Nothing is really wrong...
    1.97 +    return NS_OK;
    1.98 +  }
    1.99 +  
   1.100 +  nsCOMPtr<nsIInputStream> inStream;
   1.101 +  NS_NewLocalFileInputStream(getter_AddRefs(inStream), theFile);
   1.102 +
   1.103 +  nsCOMPtr<nsIUnicharInputStream> convStream;
   1.104 +  res = nsSimpleUnicharStreamFactory::GetInstance()->
   1.105 +    CreateInstanceFromUTF8Stream(inStream, getter_AddRefs(convStream));
   1.106 +  if(NS_FAILED(res)) return res;
   1.107 +  
   1.108 +  // we're rereading to get rid of the old data  -- we shouldn't have any, but...
   1.109 +  mDictionaryTable.Clear();
   1.110 +
   1.111 +  char16_t c;
   1.112 +  uint32_t nRead;
   1.113 +  bool done = false;
   1.114 +  do{  // read each line of text into the string array.
   1.115 +    if( (NS_OK != convStream->Read(&c, 1, &nRead)) || (nRead != 1)) break;
   1.116 +    while(!done && ((c == '\n') || (c == '\r'))){
   1.117 +      if( (NS_OK != convStream->Read(&c, 1, &nRead)) || (nRead != 1)) done = true;
   1.118 +    }
   1.119 +    if (!done){ 
   1.120 +      nsAutoString word;
   1.121 +      while((c != '\n') && (c != '\r') && !done){
   1.122 +        word.Append(c);
   1.123 +        if( (NS_OK != convStream->Read(&c, 1, &nRead)) || (nRead != 1)) done = true;
   1.124 +      }
   1.125 +      mDictionaryTable.PutEntry(word.get());
   1.126 +    }
   1.127 +  } while(!done);
   1.128 +  mDirty = false;
   1.129 +  
   1.130 +  return res;
   1.131 +}
   1.132 +
   1.133 +// A little helper function to add the key to the list.
   1.134 +// This is not threadsafe, and only safe if the consumer does not 
   1.135 +// modify the list.
   1.136 +static PLDHashOperator
   1.137 +AddHostToStringArray(nsUnicharPtrHashKey *aEntry, void *aArg)
   1.138 +{
   1.139 +  static_cast<nsTArray<nsString>*>(aArg)->AppendElement(nsDependentString(aEntry->GetKey()));
   1.140 +  return PL_DHASH_NEXT;
   1.141 +}
   1.142 +
   1.143 +/* void Save (); */
   1.144 +NS_IMETHODIMP mozPersonalDictionary::Save()
   1.145 +{
   1.146 +  nsCOMPtr<nsIFile> theFile;
   1.147 +  nsresult res;
   1.148 +
   1.149 +  if(!mDirty) return NS_OK;
   1.150 +
   1.151 +  //FIXME Deinst  -- get dictionary name from prefs;
   1.152 +  res = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(theFile));
   1.153 +  if(NS_FAILED(res)) return res;
   1.154 +  if(!theFile)return NS_ERROR_FAILURE;
   1.155 +  res = theFile->Append(NS_LITERAL_STRING(MOZ_PERSONAL_DICT_NAME));
   1.156 +  if(NS_FAILED(res)) return res;
   1.157 +
   1.158 +  nsCOMPtr<nsIOutputStream> outStream;
   1.159 +  NS_NewSafeLocalFileOutputStream(getter_AddRefs(outStream), theFile, PR_CREATE_FILE | PR_WRONLY | PR_TRUNCATE ,0664);
   1.160 +
   1.161 +  // get a buffered output stream 4096 bytes big, to optimize writes
   1.162 +  nsCOMPtr<nsIOutputStream> bufferedOutputStream;
   1.163 +  res = NS_NewBufferedOutputStream(getter_AddRefs(bufferedOutputStream), outStream, 4096);
   1.164 +  if (NS_FAILED(res)) return res;
   1.165 +
   1.166 +  nsTArray<nsString> array(mDictionaryTable.Count());
   1.167 +  mDictionaryTable.EnumerateEntries(AddHostToStringArray, &array);
   1.168 +
   1.169 +  uint32_t bytesWritten;
   1.170 +  nsAutoCString utf8Key;
   1.171 +  for (uint32_t i = 0; i < array.Length(); ++i ) {
   1.172 +    CopyUTF16toUTF8(array[i], utf8Key);
   1.173 +
   1.174 +    bufferedOutputStream->Write(utf8Key.get(), utf8Key.Length(), &bytesWritten);
   1.175 +    bufferedOutputStream->Write("\n", 1, &bytesWritten);
   1.176 +  }
   1.177 +  nsCOMPtr<nsISafeOutputStream> safeStream = do_QueryInterface(bufferedOutputStream);
   1.178 +  NS_ASSERTION(safeStream, "expected a safe output stream!");
   1.179 +  if (safeStream) {
   1.180 +    res = safeStream->Finish();
   1.181 +    if (NS_FAILED(res)) {
   1.182 +      NS_WARNING("failed to save personal dictionary file! possible data loss");
   1.183 +    }
   1.184 +  }
   1.185 +  return res;
   1.186 +}
   1.187 +
   1.188 +/* readonly attribute nsIStringEnumerator GetWordList() */
   1.189 +NS_IMETHODIMP mozPersonalDictionary::GetWordList(nsIStringEnumerator **aWords)
   1.190 +{
   1.191 +  NS_ENSURE_ARG_POINTER(aWords);
   1.192 +  *aWords = nullptr;
   1.193 +
   1.194 +  nsTArray<nsString> *array = new nsTArray<nsString>(mDictionaryTable.Count());
   1.195 +  if (!array)
   1.196 +    return NS_ERROR_OUT_OF_MEMORY;
   1.197 +
   1.198 +  mDictionaryTable.EnumerateEntries(AddHostToStringArray, array);
   1.199 +
   1.200 +  array->Sort();
   1.201 +
   1.202 +  return NS_NewAdoptingStringEnumerator(aWords, array);
   1.203 +}
   1.204 +
   1.205 +/* boolean Check (in wstring word, in wstring language); */
   1.206 +NS_IMETHODIMP mozPersonalDictionary::Check(const char16_t *aWord, const char16_t *aLanguage, bool *aResult)
   1.207 +{
   1.208 +  NS_ENSURE_ARG_POINTER(aWord);
   1.209 +  NS_ENSURE_ARG_POINTER(aResult);
   1.210 +
   1.211 +  *aResult = (mDictionaryTable.GetEntry(aWord) || mIgnoreTable.GetEntry(aWord));
   1.212 +  return NS_OK;
   1.213 +}
   1.214 +
   1.215 +/* void AddWord (in wstring word); */
   1.216 +NS_IMETHODIMP mozPersonalDictionary::AddWord(const char16_t *aWord, const char16_t *aLang)
   1.217 +{
   1.218 +  mDictionaryTable.PutEntry(aWord);
   1.219 +  mDirty = true;
   1.220 +  return NS_OK;
   1.221 +}
   1.222 +
   1.223 +/* void RemoveWord (in wstring word); */
   1.224 +NS_IMETHODIMP mozPersonalDictionary::RemoveWord(const char16_t *aWord, const char16_t *aLang)
   1.225 +{
   1.226 +  mDictionaryTable.RemoveEntry(aWord);
   1.227 +  mDirty = true;
   1.228 +  return NS_OK;
   1.229 +}
   1.230 +
   1.231 +/* void IgnoreWord (in wstring word); */
   1.232 +NS_IMETHODIMP mozPersonalDictionary::IgnoreWord(const char16_t *aWord)
   1.233 +{
   1.234 +  // avoid adding duplicate words to the ignore list
   1.235 +  if (aWord && !mIgnoreTable.GetEntry(aWord)) 
   1.236 +    mIgnoreTable.PutEntry(aWord);
   1.237 +  return NS_OK;
   1.238 +}
   1.239 +
   1.240 +/* void EndSession (); */
   1.241 +NS_IMETHODIMP mozPersonalDictionary::EndSession()
   1.242 +{
   1.243 +  Save(); // save any custom words at the end of a spell check session
   1.244 +  mIgnoreTable.Clear();
   1.245 +  return NS_OK;
   1.246 +}
   1.247 +
   1.248 +/* void AddCorrection (in wstring word, in wstring correction); */
   1.249 +NS_IMETHODIMP mozPersonalDictionary::AddCorrection(const char16_t *word, const char16_t *correction, const char16_t *lang)
   1.250 +{
   1.251 +    return NS_ERROR_NOT_IMPLEMENTED;
   1.252 +}
   1.253 +
   1.254 +/* void RemoveCorrection (in wstring word, in wstring correction); */
   1.255 +NS_IMETHODIMP mozPersonalDictionary::RemoveCorrection(const char16_t *word, const char16_t *correction, const char16_t *lang)
   1.256 +{
   1.257 +    return NS_ERROR_NOT_IMPLEMENTED;
   1.258 +}
   1.259 +
   1.260 +/* void GetCorrection (in wstring word, [array, size_is (count)] out wstring words, out uint32_t count); */
   1.261 +NS_IMETHODIMP mozPersonalDictionary::GetCorrection(const char16_t *word, char16_t ***words, uint32_t *count)
   1.262 +{
   1.263 +    return NS_ERROR_NOT_IMPLEMENTED;
   1.264 +}
   1.265 +
   1.266 +/* void observe (in nsISupports aSubject, in string aTopic, in wstring aData); */
   1.267 +NS_IMETHODIMP mozPersonalDictionary::Observe(nsISupports *aSubject, const char *aTopic, const char16_t *aData)
   1.268 +{
   1.269 +  if (!nsCRT::strcmp(aTopic, "profile-do-change")) {
   1.270 +    Load();  // load automatically clears out the existing dictionary table
   1.271 +  } else if (!nsCRT::strcmp(aTopic, "profile-before-change")) {
   1.272 +    Save();
   1.273 +  }
   1.274 +
   1.275 +  return NS_OK;
   1.276 +}
   1.277 +

mercurial