Bug 470455 - test_database_sync_embed_visits.js leaks, r=sdwilsh
[wine-gecko.git] / extensions / spellcheck / src / mozPersonalDictionary.cpp
blobcb7c18de2117186f3816f9f2434d0d8e2dd21d26
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
15 * The Original Code is Mozilla Spellchecker Component.
17 * The Initial Developer of the Original Code is
18 * David Einstein.
19 * Portions created by the Initial Developer are Copyright (C) 2001
20 * the Initial Developer. All Rights Reserved.
22 * Contributor(s): David Einstein <Deinst@world.std.com>
24 * Alternatively, the contents of this file may be used under the terms of
25 * either the GNU General Public License Version 2 or later (the "GPL"), or
26 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 * in which case the provisions of the GPL or the LGPL are applicable instead
28 * of those above. If you wish to allow use of your version of this file only
29 * under the terms of either the GPL or the LGPL, and not to allow others to
30 * use your version of this file under the terms of the MPL, indicate your
31 * decision by deleting the provisions above and replace them with the notice
32 * and other provisions required by the GPL or the LGPL. If you do not delete
33 * the provisions above, a recipient may use your version of this file under
34 * the terms of any one of the MPL, the GPL or the LGPL.
36 * ***** END LICENSE BLOCK ***** */
38 #include "mozPersonalDictionary.h"
39 #include "nsIUnicharInputStream.h"
40 #include "nsReadableUtils.h"
41 #include "nsIFile.h"
42 #include "nsAppDirectoryServiceDefs.h"
43 #include "nsICharsetConverterManager.h"
44 #include "nsICharsetAlias.h"
45 #include "nsIObserverService.h"
46 #include "nsIPrefService.h"
47 #include "nsIPrefBranch.h"
48 #include "nsIPrefBranch2.h"
49 #include "nsIWeakReference.h"
50 #include "nsCRT.h"
51 #include "nsNetUtil.h"
52 #include "nsStringEnumerator.h"
53 #include "nsUnicharInputStream.h"
55 #define MOZ_PERSONAL_DICT_NAME "persdict.dat"
57 const int kMaxWordLen=256;
59 /**
60 * This is the most braindead implementation of a personal dictionary possible.
61 * There is not much complexity needed, though. It could be made much faster,
62 * and probably should, but I don't see much need for more in terms of interface.
64 * Allowing personal words to be associated with only certain dictionaries maybe.
66 * TODO:
67 * Implement the suggestion record.
71 NS_IMPL_ISUPPORTS3(mozPersonalDictionary, mozIPersonalDictionary, nsIObserver, nsISupportsWeakReference)
73 mozPersonalDictionary::mozPersonalDictionary()
74 : mDirty(PR_FALSE)
78 mozPersonalDictionary::~mozPersonalDictionary()
82 nsresult mozPersonalDictionary::Init()
84 if (!mDictionaryTable.Init() || !mIgnoreTable.Init())
85 return NS_ERROR_OUT_OF_MEMORY;
87 nsresult rv;
88 nsCOMPtr<nsIObserverService> svc =
89 do_GetService("@mozilla.org/observer-service;1", &rv);
91 if (NS_SUCCEEDED(rv) && svc)
92 rv = svc->AddObserver(this, "profile-do-change", PR_TRUE); // we want to reload the dictionary if the profile switches
94 if (NS_FAILED(rv)) return rv;
96 Load();
98 return NS_OK;
101 /* void Load (); */
102 NS_IMETHODIMP mozPersonalDictionary::Load()
104 //FIXME Deinst -- get dictionary name from prefs;
105 nsresult res;
106 nsCOMPtr<nsIFile> theFile;
107 PRBool dictExists;
109 res = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(theFile));
110 if(NS_FAILED(res)) return res;
111 if(!theFile)return NS_ERROR_FAILURE;
112 res = theFile->Append(NS_LITERAL_STRING(MOZ_PERSONAL_DICT_NAME));
113 if(NS_FAILED(res)) return res;
114 res = theFile->Exists(&dictExists);
115 if(NS_FAILED(res)) return res;
117 if (!dictExists) {
118 // Nothing is really wrong...
119 return NS_OK;
122 nsCOMPtr<nsIInputStream> inStream;
123 NS_NewLocalFileInputStream(getter_AddRefs(inStream), theFile);
125 nsCOMPtr<nsIUnicharInputStream> convStream;
126 res = nsSimpleUnicharStreamFactory::GetInstance()->
127 CreateInstanceFromUTF8Stream(inStream, getter_AddRefs(convStream));
128 if(NS_FAILED(res)) return res;
130 // we're rereading to get rid of the old data -- we shouldn't have any, but...
131 mDictionaryTable.Clear();
133 PRUnichar c;
134 PRUint32 nRead;
135 PRBool done = PR_FALSE;
136 do{ // read each line of text into the string array.
137 if( (NS_OK != convStream->Read(&c, 1, &nRead)) || (nRead != 1)) break;
138 while(!done && ((c == '\n') || (c == '\r'))){
139 if( (NS_OK != convStream->Read(&c, 1, &nRead)) || (nRead != 1)) done = PR_TRUE;
141 if (!done){
142 nsAutoString word;
143 while((c != '\n') && (c != '\r') && !done){
144 word.Append(c);
145 if( (NS_OK != convStream->Read(&c, 1, &nRead)) || (nRead != 1)) done = PR_TRUE;
147 mDictionaryTable.PutEntry(word.get());
149 } while(!done);
150 mDirty = PR_FALSE;
152 return res;
155 // A little helper function to add the key to the list.
156 // This is not threadsafe, and only safe if the consumer does not
157 // modify the list.
158 static PLDHashOperator
159 AddHostToStringArray(nsUniCharEntry *aEntry, void *aArg)
161 static_cast<nsStringArray*>(aArg)->AppendString(nsDependentString(aEntry->GetKey()));
162 return PL_DHASH_NEXT;
165 /* void Save (); */
166 NS_IMETHODIMP mozPersonalDictionary::Save()
168 nsCOMPtr<nsIFile> theFile;
169 nsresult res;
171 if(!mDirty) return NS_OK;
173 //FIXME Deinst -- get dictionary name from prefs;
174 res = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(theFile));
175 if(NS_FAILED(res)) return res;
176 if(!theFile)return NS_ERROR_FAILURE;
177 res = theFile->Append(NS_LITERAL_STRING(MOZ_PERSONAL_DICT_NAME));
178 if(NS_FAILED(res)) return res;
180 nsCOMPtr<nsIOutputStream> outStream;
181 NS_NewLocalFileOutputStream(getter_AddRefs(outStream), theFile, PR_CREATE_FILE | PR_WRONLY | PR_TRUNCATE ,0664);
183 // get a buffered output stream 4096 bytes big, to optimize writes
184 nsCOMPtr<nsIOutputStream> bufferedOutputStream;
185 res = NS_NewBufferedOutputStream(getter_AddRefs(bufferedOutputStream), outStream, 4096);
186 if (NS_FAILED(res)) return res;
188 nsStringArray array(mDictionaryTable.Count());
189 mDictionaryTable.EnumerateEntries(AddHostToStringArray, &array);
191 PRUint32 bytesWritten;
192 nsCAutoString utf8Key;
193 for (PRInt32 i = 0; i < array.Count(); ++i ) {
194 const nsString *key = array[i];
195 CopyUTF16toUTF8(*key, utf8Key);
197 bufferedOutputStream->Write(utf8Key.get(), utf8Key.Length(), &bytesWritten);
198 bufferedOutputStream->Write("\n", 1, &bytesWritten);
200 return res;
203 /* readonly attribute nsIStringEnumerator GetWordList() */
204 NS_IMETHODIMP mozPersonalDictionary::GetWordList(nsIStringEnumerator **aWords)
206 NS_ENSURE_ARG_POINTER(aWords);
207 *aWords = nsnull;
209 nsStringArray *array = new nsStringArray(mDictionaryTable.Count());
210 if (!array)
211 return NS_ERROR_OUT_OF_MEMORY;
213 mDictionaryTable.EnumerateEntries(AddHostToStringArray, array);
215 array->Sort();
217 return NS_NewAdoptingStringEnumerator(aWords, array);
220 /* boolean Check (in wstring word, in wstring language); */
221 NS_IMETHODIMP mozPersonalDictionary::Check(const PRUnichar *aWord, const PRUnichar *aLanguage, PRBool *aResult)
223 NS_ENSURE_ARG_POINTER(aWord);
224 NS_ENSURE_ARG_POINTER(aResult);
226 *aResult = (mDictionaryTable.GetEntry(aWord) || mIgnoreTable.GetEntry(aWord));
227 return NS_OK;
230 /* void AddWord (in wstring word); */
231 NS_IMETHODIMP mozPersonalDictionary::AddWord(const PRUnichar *aWord, const PRUnichar *aLang)
233 mDictionaryTable.PutEntry(aWord);
234 mDirty = PR_TRUE;
235 return NS_OK;
238 /* void RemoveWord (in wstring word); */
239 NS_IMETHODIMP mozPersonalDictionary::RemoveWord(const PRUnichar *aWord, const PRUnichar *aLang)
241 mDictionaryTable.RemoveEntry(aWord);
242 mDirty = PR_TRUE;
243 return NS_OK;
246 /* void IgnoreWord (in wstring word); */
247 NS_IMETHODIMP mozPersonalDictionary::IgnoreWord(const PRUnichar *aWord)
249 // avoid adding duplicate words to the ignore list
250 if (aWord && !mIgnoreTable.GetEntry(aWord))
251 mIgnoreTable.PutEntry(aWord);
252 return NS_OK;
255 /* void EndSession (); */
256 NS_IMETHODIMP mozPersonalDictionary::EndSession()
258 Save(); // save any custom words at the end of a spell check session
259 mIgnoreTable.Clear();
260 return NS_OK;
263 /* void AddCorrection (in wstring word, in wstring correction); */
264 NS_IMETHODIMP mozPersonalDictionary::AddCorrection(const PRUnichar *word, const PRUnichar *correction, const PRUnichar *lang)
266 return NS_ERROR_NOT_IMPLEMENTED;
269 /* void RemoveCorrection (in wstring word, in wstring correction); */
270 NS_IMETHODIMP mozPersonalDictionary::RemoveCorrection(const PRUnichar *word, const PRUnichar *correction, const PRUnichar *lang)
272 return NS_ERROR_NOT_IMPLEMENTED;
275 /* void GetCorrection (in wstring word, [array, size_is (count)] out wstring words, out PRUint32 count); */
276 NS_IMETHODIMP mozPersonalDictionary::GetCorrection(const PRUnichar *word, PRUnichar ***words, PRUint32 *count)
278 return NS_ERROR_NOT_IMPLEMENTED;
281 /* void observe (in nsISupports aSubject, in string aTopic, in wstring aData); */
282 NS_IMETHODIMP mozPersonalDictionary::Observe(nsISupports *aSubject, const char *aTopic, const PRUnichar *aData)
284 if (!nsCRT::strcmp(aTopic, "profile-do-change")) {
285 Load(); // load automatically clears out the existing dictionary table
288 return NS_OK;