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
15 * The Original Code is Mozilla Spellchecker Component.
17 * The Initial Developer of the Original Code is
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"
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"
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;
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.
67 * Implement the suggestion record.
71 NS_IMPL_ISUPPORTS3(mozPersonalDictionary
, mozIPersonalDictionary
, nsIObserver
, nsISupportsWeakReference
)
73 mozPersonalDictionary::mozPersonalDictionary()
78 mozPersonalDictionary::~mozPersonalDictionary()
82 nsresult
mozPersonalDictionary::Init()
84 if (!mDictionaryTable
.Init() || !mIgnoreTable
.Init())
85 return NS_ERROR_OUT_OF_MEMORY
;
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
;
102 NS_IMETHODIMP
mozPersonalDictionary::Load()
104 //FIXME Deinst -- get dictionary name from prefs;
106 nsCOMPtr
<nsIFile
> theFile
;
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
;
118 // Nothing is really wrong...
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();
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
;
143 while((c
!= '\n') && (c
!= '\r') && !done
){
145 if( (NS_OK
!= convStream
->Read(&c
, 1, &nRead
)) || (nRead
!= 1)) done
= PR_TRUE
;
147 mDictionaryTable
.PutEntry(word
.get());
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
158 static PLDHashOperator
159 AddHostToStringArray(nsUniCharEntry
*aEntry
, void *aArg
)
161 static_cast<nsStringArray
*>(aArg
)->AppendString(nsDependentString(aEntry
->GetKey()));
162 return PL_DHASH_NEXT
;
166 NS_IMETHODIMP
mozPersonalDictionary::Save()
168 nsCOMPtr
<nsIFile
> theFile
;
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
);
203 /* readonly attribute nsIStringEnumerator GetWordList() */
204 NS_IMETHODIMP
mozPersonalDictionary::GetWordList(nsIStringEnumerator
**aWords
)
206 NS_ENSURE_ARG_POINTER(aWords
);
209 nsStringArray
*array
= new nsStringArray(mDictionaryTable
.Count());
211 return NS_ERROR_OUT_OF_MEMORY
;
213 mDictionaryTable
.EnumerateEntries(AddHostToStringArray
, array
);
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
));
230 /* void AddWord (in wstring word); */
231 NS_IMETHODIMP
mozPersonalDictionary::AddWord(const PRUnichar
*aWord
, const PRUnichar
*aLang
)
233 mDictionaryTable
.PutEntry(aWord
);
238 /* void RemoveWord (in wstring word); */
239 NS_IMETHODIMP
mozPersonalDictionary::RemoveWord(const PRUnichar
*aWord
, const PRUnichar
*aLang
)
241 mDictionaryTable
.RemoveEntry(aWord
);
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
);
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();
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