1*cdf0e10cSrcweir /*************************************************************************
2*cdf0e10cSrcweir  *
3*cdf0e10cSrcweir  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4*cdf0e10cSrcweir  *
5*cdf0e10cSrcweir  * Copyright 2000, 2010 Oracle and/or its affiliates.
6*cdf0e10cSrcweir  *
7*cdf0e10cSrcweir  * OpenOffice.org - a multi-platform office productivity suite
8*cdf0e10cSrcweir  *
9*cdf0e10cSrcweir  * This file is part of OpenOffice.org.
10*cdf0e10cSrcweir  *
11*cdf0e10cSrcweir  * OpenOffice.org is free software: you can redistribute it and/or modify
12*cdf0e10cSrcweir  * it under the terms of the GNU Lesser General Public License version 3
13*cdf0e10cSrcweir  * only, as published by the Free Software Foundation.
14*cdf0e10cSrcweir  *
15*cdf0e10cSrcweir  * OpenOffice.org is distributed in the hope that it will be useful,
16*cdf0e10cSrcweir  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17*cdf0e10cSrcweir  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18*cdf0e10cSrcweir  * GNU Lesser General Public License version 3 for more details
19*cdf0e10cSrcweir  * (a copy is included in the LICENSE file that accompanied this code).
20*cdf0e10cSrcweir  *
21*cdf0e10cSrcweir  * You should have received a copy of the GNU Lesser General Public License
22*cdf0e10cSrcweir  * version 3 along with OpenOffice.org.  If not, see
23*cdf0e10cSrcweir  * <http://www.openoffice.org/license.html>
24*cdf0e10cSrcweir  * for a copy of the LGPLv3 License.
25*cdf0e10cSrcweir  *
26*cdf0e10cSrcweir  ************************************************************************/
27*cdf0e10cSrcweir 
28*cdf0e10cSrcweir // MARKER(update_precomp.py): autogen include statement, do not remove
29*cdf0e10cSrcweir #include "precompiled_lingucomponent.hxx"
30*cdf0e10cSrcweir 
31*cdf0e10cSrcweir #include <com/sun/star/uno/Reference.h>
32*cdf0e10cSrcweir #include <com/sun/star/linguistic2/XSearchableDictionaryList.hpp>
33*cdf0e10cSrcweir 
34*cdf0e10cSrcweir #include <com/sun/star/linguistic2/SpellFailure.hpp>
35*cdf0e10cSrcweir #include <cppuhelper/factory.hxx>	// helper for factories
36*cdf0e10cSrcweir #include <com/sun/star/registry/XRegistryKey.hpp>
37*cdf0e10cSrcweir #include <tools/debug.hxx>
38*cdf0e10cSrcweir #include <unotools/processfactory.hxx>
39*cdf0e10cSrcweir #include <osl/mutex.hxx>
40*cdf0e10cSrcweir 
41*cdf0e10cSrcweir #include <lingutil.hxx>
42*cdf0e10cSrcweir #include <hunspell.hxx>
43*cdf0e10cSrcweir #include <dictmgr.hxx>
44*cdf0e10cSrcweir #include <sspellimp.hxx>
45*cdf0e10cSrcweir 
46*cdf0e10cSrcweir #include <linguistic/lngprops.hxx>
47*cdf0e10cSrcweir #include <linguistic/spelldta.hxx>
48*cdf0e10cSrcweir #include <i18npool/mslangid.hxx>
49*cdf0e10cSrcweir #include <unotools/pathoptions.hxx>
50*cdf0e10cSrcweir #include <unotools/lingucfg.hxx>
51*cdf0e10cSrcweir #include <unotools/useroptions.hxx>
52*cdf0e10cSrcweir #include <osl/file.hxx>
53*cdf0e10cSrcweir #include <rtl/ustrbuf.hxx>
54*cdf0e10cSrcweir #include <rtl/textenc.h>
55*cdf0e10cSrcweir 
56*cdf0e10cSrcweir #include <list>
57*cdf0e10cSrcweir #include <set>
58*cdf0e10cSrcweir #include <string.h>
59*cdf0e10cSrcweir 
60*cdf0e10cSrcweir using namespace utl;
61*cdf0e10cSrcweir using namespace osl;
62*cdf0e10cSrcweir using namespace rtl;
63*cdf0e10cSrcweir using namespace com::sun::star;
64*cdf0e10cSrcweir using namespace com::sun::star::beans;
65*cdf0e10cSrcweir using namespace com::sun::star::lang;
66*cdf0e10cSrcweir using namespace com::sun::star::uno;
67*cdf0e10cSrcweir using namespace com::sun::star::linguistic2;
68*cdf0e10cSrcweir using namespace linguistic;
69*cdf0e10cSrcweir 
70*cdf0e10cSrcweir // XML-header of SPELLML queries
71*cdf0e10cSrcweir #define SPELLML_HEADER "<?xml?>"
72*cdf0e10cSrcweir 
73*cdf0e10cSrcweir ///////////////////////////////////////////////////////////////////////////
74*cdf0e10cSrcweir 
75*cdf0e10cSrcweir SpellChecker::SpellChecker() :
76*cdf0e10cSrcweir 	aEvtListeners	( GetLinguMutex() )
77*cdf0e10cSrcweir {
78*cdf0e10cSrcweir     aDicts = NULL;
79*cdf0e10cSrcweir 	aDEncs = NULL;
80*cdf0e10cSrcweir 	aDLocs = NULL;
81*cdf0e10cSrcweir 	aDNames = NULL;
82*cdf0e10cSrcweir 	bDisposing = sal_False;
83*cdf0e10cSrcweir 	pPropHelper = NULL;
84*cdf0e10cSrcweir     numdict = 0;
85*cdf0e10cSrcweir }
86*cdf0e10cSrcweir 
87*cdf0e10cSrcweir 
88*cdf0e10cSrcweir SpellChecker::~SpellChecker()
89*cdf0e10cSrcweir {
90*cdf0e10cSrcweir     if (aDicts)
91*cdf0e10cSrcweir     {
92*cdf0e10cSrcweir        for (int i = 0; i < numdict; i++)
93*cdf0e10cSrcweir        {
94*cdf0e10cSrcweir             if (aDicts[i]) delete aDicts[i];
95*cdf0e10cSrcweir             aDicts[i] = NULL;
96*cdf0e10cSrcweir        }
97*cdf0e10cSrcweir        delete[] aDicts;
98*cdf0e10cSrcweir     }
99*cdf0e10cSrcweir     aDicts = NULL;
100*cdf0e10cSrcweir     numdict = 0;
101*cdf0e10cSrcweir     if (aDEncs) delete[] aDEncs;
102*cdf0e10cSrcweir     aDEncs = NULL;
103*cdf0e10cSrcweir     if (aDLocs) delete[] aDLocs;
104*cdf0e10cSrcweir     aDLocs = NULL;
105*cdf0e10cSrcweir     if (aDNames) delete[] aDNames;
106*cdf0e10cSrcweir     aDNames = NULL;
107*cdf0e10cSrcweir     if (pPropHelper)
108*cdf0e10cSrcweir         pPropHelper->RemoveAsPropListener();
109*cdf0e10cSrcweir }
110*cdf0e10cSrcweir 
111*cdf0e10cSrcweir 
112*cdf0e10cSrcweir PropertyHelper_Spell & SpellChecker::GetPropHelper_Impl()
113*cdf0e10cSrcweir {
114*cdf0e10cSrcweir 	if (!pPropHelper)
115*cdf0e10cSrcweir 	{
116*cdf0e10cSrcweir 		Reference< XPropertySet	>	xPropSet( GetLinguProperties(), UNO_QUERY );
117*cdf0e10cSrcweir 
118*cdf0e10cSrcweir 		pPropHelper	= new PropertyHelper_Spell( (XSpellChecker *) this, xPropSet );
119*cdf0e10cSrcweir 		xPropHelper = pPropHelper;
120*cdf0e10cSrcweir 		pPropHelper->AddAsPropListener();	//! after a reference is established
121*cdf0e10cSrcweir 	}
122*cdf0e10cSrcweir 	return *pPropHelper;
123*cdf0e10cSrcweir }
124*cdf0e10cSrcweir 
125*cdf0e10cSrcweir 
126*cdf0e10cSrcweir Sequence< Locale > SAL_CALL SpellChecker::getLocales()
127*cdf0e10cSrcweir 		throw(RuntimeException)
128*cdf0e10cSrcweir {
129*cdf0e10cSrcweir     MutexGuard  aGuard( GetLinguMutex() );
130*cdf0e10cSrcweir 
131*cdf0e10cSrcweir     // this routine should return the locales supported by the installed
132*cdf0e10cSrcweir     // dictionaries.
133*cdf0e10cSrcweir 
134*cdf0e10cSrcweir     if (!numdict)
135*cdf0e10cSrcweir     {
136*cdf0e10cSrcweir         SvtLinguConfig aLinguCfg;
137*cdf0e10cSrcweir 
138*cdf0e10cSrcweir         // get list of extension dictionaries-to-use
139*cdf0e10cSrcweir 		// (or better speaking: the list of dictionaries using the
140*cdf0e10cSrcweir 		// new configuration entries).
141*cdf0e10cSrcweir         std::list< SvtLinguConfigDictionaryEntry > aDics;
142*cdf0e10cSrcweir         uno::Sequence< rtl::OUString > aFormatList;
143*cdf0e10cSrcweir         aLinguCfg.GetSupportedDictionaryFormatsFor( A2OU("SpellCheckers"),
144*cdf0e10cSrcweir                 A2OU("org.openoffice.lingu.MySpellSpellChecker"), aFormatList );
145*cdf0e10cSrcweir         sal_Int32 nLen = aFormatList.getLength();
146*cdf0e10cSrcweir         for (sal_Int32 i = 0;  i < nLen;  ++i)
147*cdf0e10cSrcweir         {
148*cdf0e10cSrcweir             std::vector< SvtLinguConfigDictionaryEntry > aTmpDic(
149*cdf0e10cSrcweir                     aLinguCfg.GetActiveDictionariesByFormat( aFormatList[i] ) );
150*cdf0e10cSrcweir             aDics.insert( aDics.end(), aTmpDic.begin(), aTmpDic.end() );
151*cdf0e10cSrcweir         }
152*cdf0e10cSrcweir 
153*cdf0e10cSrcweir         //!! for compatibility with old dictionaries (the ones not using extensions
154*cdf0e10cSrcweir         //!! or new configuration entries, but still using the dictionary.lst file)
155*cdf0e10cSrcweir 		//!! Get the list of old style spell checking dictionaries to use...
156*cdf0e10cSrcweir         std::vector< SvtLinguConfigDictionaryEntry > aOldStyleDics(
157*cdf0e10cSrcweir 				GetOldStyleDics( "DICT" ) );
158*cdf0e10cSrcweir 
159*cdf0e10cSrcweir 		// to prefer dictionaries with configuration entries we will only
160*cdf0e10cSrcweir 		// use those old style dictionaries that add a language that
161*cdf0e10cSrcweir 		// is not yet supported by the list od new style dictionaries
162*cdf0e10cSrcweir 		MergeNewStyleDicsAndOldStyleDics( aDics, aOldStyleDics );
163*cdf0e10cSrcweir 
164*cdf0e10cSrcweir         numdict = aDics.size();
165*cdf0e10cSrcweir         if (numdict)
166*cdf0e10cSrcweir         {
167*cdf0e10cSrcweir             // get supported locales from the dictionaries-to-use...
168*cdf0e10cSrcweir             sal_Int32 k = 0;
169*cdf0e10cSrcweir             std::set< rtl::OUString, lt_rtl_OUString > aLocaleNamesSet;
170*cdf0e10cSrcweir             std::list< SvtLinguConfigDictionaryEntry >::const_iterator aDictIt;
171*cdf0e10cSrcweir             for (aDictIt = aDics.begin();  aDictIt != aDics.end();  ++aDictIt)
172*cdf0e10cSrcweir             {
173*cdf0e10cSrcweir                 uno::Sequence< rtl::OUString > aLocaleNames( aDictIt->aLocaleNames );
174*cdf0e10cSrcweir                 sal_Int32 nLen2 = aLocaleNames.getLength();
175*cdf0e10cSrcweir                 for (k = 0;  k < nLen2;  ++k)
176*cdf0e10cSrcweir                 {
177*cdf0e10cSrcweir                     aLocaleNamesSet.insert( aLocaleNames[k] );
178*cdf0e10cSrcweir                 }
179*cdf0e10cSrcweir             }
180*cdf0e10cSrcweir             // ... and add them to the resulting sequence
181*cdf0e10cSrcweir             aSuppLocales.realloc( aLocaleNamesSet.size() );
182*cdf0e10cSrcweir             std::set< rtl::OUString, lt_rtl_OUString >::const_iterator aItB;
183*cdf0e10cSrcweir             k = 0;
184*cdf0e10cSrcweir             for (aItB = aLocaleNamesSet.begin();  aItB != aLocaleNamesSet.end();  ++aItB)
185*cdf0e10cSrcweir             {
186*cdf0e10cSrcweir 				Locale aTmp( MsLangId::convertLanguageToLocale(
187*cdf0e10cSrcweir 						MsLangId::convertIsoStringToLanguage( *aItB )));
188*cdf0e10cSrcweir                 aSuppLocales[k++] = aTmp;
189*cdf0e10cSrcweir             }
190*cdf0e10cSrcweir 
191*cdf0e10cSrcweir             //! For each dictionary and each locale we need a seperate entry.
192*cdf0e10cSrcweir             //! If this results in more than one dictionary per locale than (for now)
193*cdf0e10cSrcweir 			//! it is undefined which dictionary gets used.
194*cdf0e10cSrcweir 			//! In the future the implementation should support using several dictionaries
195*cdf0e10cSrcweir 			//! for one locale.
196*cdf0e10cSrcweir 			numdict = 0;
197*cdf0e10cSrcweir             for (aDictIt = aDics.begin();  aDictIt != aDics.end();  ++aDictIt)
198*cdf0e10cSrcweir 				numdict = numdict + aDictIt->aLocaleNames.getLength();
199*cdf0e10cSrcweir 
200*cdf0e10cSrcweir             // add dictionary information
201*cdf0e10cSrcweir             aDicts  = new Hunspell* [numdict];
202*cdf0e10cSrcweir             aDEncs  = new rtl_TextEncoding [numdict];
203*cdf0e10cSrcweir             aDLocs  = new Locale [numdict];
204*cdf0e10cSrcweir             aDNames = new OUString [numdict];
205*cdf0e10cSrcweir             k = 0;
206*cdf0e10cSrcweir             for (aDictIt = aDics.begin();  aDictIt != aDics.end();  ++aDictIt)
207*cdf0e10cSrcweir             {
208*cdf0e10cSrcweir                 if (aDictIt->aLocaleNames.getLength() > 0 &&
209*cdf0e10cSrcweir                     aDictIt->aLocations.getLength() > 0)
210*cdf0e10cSrcweir                 {
211*cdf0e10cSrcweir                     uno::Sequence< rtl::OUString > aLocaleNames( aDictIt->aLocaleNames );
212*cdf0e10cSrcweir                     sal_Int32 nLocales = aLocaleNames.getLength();
213*cdf0e10cSrcweir 
214*cdf0e10cSrcweir                     // currently only one language per dictionary is supported in the actual implementation...
215*cdf0e10cSrcweir                     // Thus here we work-around this by adding the same dictionary several times.
216*cdf0e10cSrcweir                     // Once for each of it's supported locales.
217*cdf0e10cSrcweir                     for (sal_Int32 i = 0;  i < nLocales;  ++i)
218*cdf0e10cSrcweir                     {
219*cdf0e10cSrcweir                         aDicts[k]  = NULL;
220*cdf0e10cSrcweir                         aDEncs[k]  = RTL_TEXTENCODING_DONTKNOW;
221*cdf0e10cSrcweir                         aDLocs[k]  = MsLangId::convertLanguageToLocale(
222*cdf0e10cSrcweir                                         MsLangId::convertIsoStringToLanguage( aLocaleNames[i] ));
223*cdf0e10cSrcweir                         // also both files have to be in the same directory and the
224*cdf0e10cSrcweir                         // file names must only differ in the extension (.aff/.dic).
225*cdf0e10cSrcweir                         // Thus we use the first location only and strip the extension part.
226*cdf0e10cSrcweir                         rtl::OUString aLocation = aDictIt->aLocations[0];
227*cdf0e10cSrcweir                         sal_Int32 nPos = aLocation.lastIndexOf( '.' );
228*cdf0e10cSrcweir                         aLocation = aLocation.copy( 0, nPos );
229*cdf0e10cSrcweir                         aDNames[k] = aLocation;
230*cdf0e10cSrcweir 
231*cdf0e10cSrcweir                         ++k;
232*cdf0e10cSrcweir                     }
233*cdf0e10cSrcweir                 }
234*cdf0e10cSrcweir             }
235*cdf0e10cSrcweir             DBG_ASSERT( k == numdict, "index mismatch?" );
236*cdf0e10cSrcweir         }
237*cdf0e10cSrcweir         else
238*cdf0e10cSrcweir         {
239*cdf0e10cSrcweir             /* no dictionary found so register no dictionaries */
240*cdf0e10cSrcweir             numdict = 0;
241*cdf0e10cSrcweir             aDicts  = NULL;
242*cdf0e10cSrcweir             aDEncs  = NULL;
243*cdf0e10cSrcweir             aDLocs  = NULL;
244*cdf0e10cSrcweir             aDNames = NULL;
245*cdf0e10cSrcweir             aSuppLocales.realloc(0);
246*cdf0e10cSrcweir         }
247*cdf0e10cSrcweir     }
248*cdf0e10cSrcweir 
249*cdf0e10cSrcweir 	return aSuppLocales;
250*cdf0e10cSrcweir }
251*cdf0e10cSrcweir 
252*cdf0e10cSrcweir 
253*cdf0e10cSrcweir sal_Bool SAL_CALL SpellChecker::hasLocale(const Locale& rLocale)
254*cdf0e10cSrcweir 		throw(RuntimeException)
255*cdf0e10cSrcweir {
256*cdf0e10cSrcweir 	MutexGuard	aGuard( GetLinguMutex() );
257*cdf0e10cSrcweir 
258*cdf0e10cSrcweir 	sal_Bool bRes = sal_False;
259*cdf0e10cSrcweir 	if (!aSuppLocales.getLength())
260*cdf0e10cSrcweir 		getLocales();
261*cdf0e10cSrcweir 
262*cdf0e10cSrcweir 	sal_Int32 nLen = aSuppLocales.getLength();
263*cdf0e10cSrcweir 	for (sal_Int32 i = 0;  i < nLen;  ++i)
264*cdf0e10cSrcweir 	{
265*cdf0e10cSrcweir 		const Locale *pLocale = aSuppLocales.getConstArray();
266*cdf0e10cSrcweir 		if (rLocale == pLocale[i])
267*cdf0e10cSrcweir 		{
268*cdf0e10cSrcweir 			bRes = sal_True;
269*cdf0e10cSrcweir 			break;
270*cdf0e10cSrcweir 		}
271*cdf0e10cSrcweir 	}
272*cdf0e10cSrcweir 	return bRes;
273*cdf0e10cSrcweir }
274*cdf0e10cSrcweir 
275*cdf0e10cSrcweir 
276*cdf0e10cSrcweir sal_Int16 SpellChecker::GetSpellFailure( const OUString &rWord, const Locale &rLocale )
277*cdf0e10cSrcweir {
278*cdf0e10cSrcweir     Hunspell * pMS = NULL;
279*cdf0e10cSrcweir     rtl_TextEncoding eEnc = RTL_TEXTENCODING_DONTKNOW;
280*cdf0e10cSrcweir 
281*cdf0e10cSrcweir 	// initialize a myspell object for each dictionary once
282*cdf0e10cSrcweir     // (note: mutex is held higher up in isValid)
283*cdf0e10cSrcweir 
284*cdf0e10cSrcweir 	sal_Int16 nRes = -1;
285*cdf0e10cSrcweir 
286*cdf0e10cSrcweir     // first handle smart quotes both single and double
287*cdf0e10cSrcweir 	OUStringBuffer rBuf(rWord);
288*cdf0e10cSrcweir     sal_Int32 n = rBuf.getLength();
289*cdf0e10cSrcweir     sal_Unicode c;
290*cdf0e10cSrcweir     for (sal_Int32 ix=0; ix < n; ix++)
291*cdf0e10cSrcweir     {
292*cdf0e10cSrcweir 	    c = rBuf.charAt(ix);
293*cdf0e10cSrcweir         if ((c == 0x201C) || (c == 0x201D))
294*cdf0e10cSrcweir             rBuf.setCharAt(ix,(sal_Unicode)0x0022);
295*cdf0e10cSrcweir         if ((c == 0x2018) || (c == 0x2019))
296*cdf0e10cSrcweir             rBuf.setCharAt(ix,(sal_Unicode)0x0027);
297*cdf0e10cSrcweir     }
298*cdf0e10cSrcweir     OUString nWord(rBuf.makeStringAndClear());
299*cdf0e10cSrcweir 
300*cdf0e10cSrcweir 	if (n)
301*cdf0e10cSrcweir 	{
302*cdf0e10cSrcweir         for (sal_Int32 i = 0; i < numdict; ++i)
303*cdf0e10cSrcweir         {
304*cdf0e10cSrcweir             pMS = NULL;
305*cdf0e10cSrcweir             eEnc = RTL_TEXTENCODING_DONTKNOW;
306*cdf0e10cSrcweir 
307*cdf0e10cSrcweir             if (rLocale == aDLocs[i])
308*cdf0e10cSrcweir             {
309*cdf0e10cSrcweir                 if (!aDicts[i])
310*cdf0e10cSrcweir                 {
311*cdf0e10cSrcweir                     OUString dicpath = aDNames[i] + A2OU(".dic");
312*cdf0e10cSrcweir                     OUString affpath = aDNames[i] + A2OU(".aff");
313*cdf0e10cSrcweir                     OUString dict;
314*cdf0e10cSrcweir                     OUString aff;
315*cdf0e10cSrcweir                     osl::FileBase::getSystemPathFromFileURL(dicpath,dict);
316*cdf0e10cSrcweir                     osl::FileBase::getSystemPathFromFileURL(affpath,aff);
317*cdf0e10cSrcweir                     OString aTmpaff(OU2ENC(aff,osl_getThreadTextEncoding()));
318*cdf0e10cSrcweir                     OString aTmpdict(OU2ENC(dict,osl_getThreadTextEncoding()));
319*cdf0e10cSrcweir 
320*cdf0e10cSrcweir #if defined(WNT)
321*cdf0e10cSrcweir                     // workaround for Windows specifc problem that the
322*cdf0e10cSrcweir                     // path length in calls to 'fopen' is limted to somewhat
323*cdf0e10cSrcweir                     // about 120+ characters which will usually be exceed when
324*cdf0e10cSrcweir                     // using dictionaries as extensions.
325*cdf0e10cSrcweir                     aTmpaff = Win_GetShortPathName( aff );
326*cdf0e10cSrcweir                     aTmpdict = Win_GetShortPathName( dict );
327*cdf0e10cSrcweir #endif
328*cdf0e10cSrcweir 
329*cdf0e10cSrcweir                     aDicts[i] = new Hunspell(aTmpaff.getStr(),aTmpdict.getStr());
330*cdf0e10cSrcweir                     aDEncs[i] = RTL_TEXTENCODING_DONTKNOW;
331*cdf0e10cSrcweir                     if (aDicts[i])
332*cdf0e10cSrcweir                         aDEncs[i] = getTextEncodingFromCharset(aDicts[i]->get_dic_encoding());
333*cdf0e10cSrcweir                 }
334*cdf0e10cSrcweir                 pMS = aDicts[i];
335*cdf0e10cSrcweir                 eEnc = aDEncs[i];
336*cdf0e10cSrcweir             }
337*cdf0e10cSrcweir 
338*cdf0e10cSrcweir             if (pMS)
339*cdf0e10cSrcweir             {
340*cdf0e10cSrcweir                 // we don't want to work with a default text encoding since following incorrect
341*cdf0e10cSrcweir                 // results may occur only for specific text and thus may be hard to notice.
342*cdf0e10cSrcweir                 // Thus better always make a clean exit here if the text encoding is in question.
343*cdf0e10cSrcweir                 // Hopefully something not working at all will raise proper attention quickly. ;-)
344*cdf0e10cSrcweir                 DBG_ASSERT( eEnc != RTL_TEXTENCODING_DONTKNOW, "failed to get text encoding! (maybe incorrect encoding string in file)" );
345*cdf0e10cSrcweir                 if (eEnc == RTL_TEXTENCODING_DONTKNOW)
346*cdf0e10cSrcweir                     return -1;
347*cdf0e10cSrcweir 
348*cdf0e10cSrcweir                 OString aWrd(OU2ENC(nWord,eEnc));
349*cdf0e10cSrcweir                 int rVal = pMS->spell((char*)aWrd.getStr());
350*cdf0e10cSrcweir                 if (rVal != 1)
351*cdf0e10cSrcweir                     nRes = SpellFailure::SPELLING_ERROR;
352*cdf0e10cSrcweir                 else
353*cdf0e10cSrcweir                     return -1;
354*cdf0e10cSrcweir                 pMS = NULL;
355*cdf0e10cSrcweir             }
356*cdf0e10cSrcweir         }
357*cdf0e10cSrcweir 	}
358*cdf0e10cSrcweir 
359*cdf0e10cSrcweir 	return nRes;
360*cdf0e10cSrcweir }
361*cdf0e10cSrcweir 
362*cdf0e10cSrcweir 
363*cdf0e10cSrcweir sal_Bool SAL_CALL SpellChecker::isValid( const OUString& rWord, const Locale& rLocale,
364*cdf0e10cSrcweir 			const PropertyValues& rProperties )
365*cdf0e10cSrcweir 		throw(IllegalArgumentException, RuntimeException)
366*cdf0e10cSrcweir {
367*cdf0e10cSrcweir 	MutexGuard	aGuard( GetLinguMutex() );
368*cdf0e10cSrcweir 
369*cdf0e10cSrcweir  	if (rLocale == Locale()  ||  !rWord.getLength())
370*cdf0e10cSrcweir 		return sal_True;
371*cdf0e10cSrcweir 
372*cdf0e10cSrcweir 	if (!hasLocale( rLocale ))
373*cdf0e10cSrcweir #ifdef LINGU_EXCEPTIONS
374*cdf0e10cSrcweir 		throw( IllegalArgumentException() );
375*cdf0e10cSrcweir #else
376*cdf0e10cSrcweir 		return sal_True;
377*cdf0e10cSrcweir #endif
378*cdf0e10cSrcweir 
379*cdf0e10cSrcweir 	// return sal_False to process SPELLML requests (they are longer than the header)
380*cdf0e10cSrcweir 	if (rWord.match(A2OU(SPELLML_HEADER), 0) && (rWord.getLength() > 10)) return sal_False;
381*cdf0e10cSrcweir 
382*cdf0e10cSrcweir 	// Get property values to be used.
383*cdf0e10cSrcweir 	// These are be the default values set in the SN_LINGU_PROPERTIES
384*cdf0e10cSrcweir 	// PropertySet which are overridden by the supplied ones from the
385*cdf0e10cSrcweir 	// last argument.
386*cdf0e10cSrcweir 	// You'll probably like to use a simplier solution than the provided
387*cdf0e10cSrcweir 	// one using the PropertyHelper_Spell.
388*cdf0e10cSrcweir 
389*cdf0e10cSrcweir 	PropertyHelper_Spell &rHelper = GetPropHelper();
390*cdf0e10cSrcweir 	rHelper.SetTmpPropVals( rProperties );
391*cdf0e10cSrcweir 
392*cdf0e10cSrcweir 	sal_Int16 nFailure = GetSpellFailure( rWord, rLocale );
393*cdf0e10cSrcweir 	if (nFailure != -1 && !rWord.match(A2OU(SPELLML_HEADER), 0))
394*cdf0e10cSrcweir 	{
395*cdf0e10cSrcweir 		sal_Int16 nLang = LocaleToLanguage( rLocale );
396*cdf0e10cSrcweir 		// postprocess result for errors that should be ignored
397*cdf0e10cSrcweir         const bool bIgnoreError =
398*cdf0e10cSrcweir                 (!rHelper.IsSpellUpperCase()  && IsUpper( rWord, nLang )) ||
399*cdf0e10cSrcweir                 (!rHelper.IsSpellWithDigits() && HasDigits( rWord )) ||
400*cdf0e10cSrcweir                 (!rHelper.IsSpellCapitalization()  &&  nFailure == SpellFailure::CAPTION_ERROR);
401*cdf0e10cSrcweir         if (bIgnoreError)
402*cdf0e10cSrcweir             nFailure = -1;
403*cdf0e10cSrcweir 	}
404*cdf0e10cSrcweir 
405*cdf0e10cSrcweir 	return (nFailure == -1);
406*cdf0e10cSrcweir }
407*cdf0e10cSrcweir 
408*cdf0e10cSrcweir 
409*cdf0e10cSrcweir Reference< XSpellAlternatives >
410*cdf0e10cSrcweir 	SpellChecker::GetProposals( const OUString &rWord, const Locale &rLocale )
411*cdf0e10cSrcweir {
412*cdf0e10cSrcweir 	// Retrieves the return values for the 'spell' function call in case
413*cdf0e10cSrcweir 	// of a misspelled word.
414*cdf0e10cSrcweir 	// Especially it may give a list of suggested (correct) words:
415*cdf0e10cSrcweir 
416*cdf0e10cSrcweir 	Reference< XSpellAlternatives > xRes;
417*cdf0e10cSrcweir     // note: mutex is held by higher up by spell which covers both
418*cdf0e10cSrcweir 
419*cdf0e10cSrcweir     Hunspell* pMS = NULL;
420*cdf0e10cSrcweir     rtl_TextEncoding eEnc = RTL_TEXTENCODING_DONTKNOW;
421*cdf0e10cSrcweir     int count = 0;
422*cdf0e10cSrcweir     int numsug = 0;
423*cdf0e10cSrcweir 
424*cdf0e10cSrcweir     // first handle smart quotes (single and double)
425*cdf0e10cSrcweir 	OUStringBuffer rBuf(rWord);
426*cdf0e10cSrcweir     sal_Int32 n = rBuf.getLength();
427*cdf0e10cSrcweir     sal_Unicode c;
428*cdf0e10cSrcweir     for (sal_Int32 ix=0; ix < n; ix++)
429*cdf0e10cSrcweir     {
430*cdf0e10cSrcweir         c = rBuf.charAt(ix);
431*cdf0e10cSrcweir         if ((c == 0x201C) || (c == 0x201D))
432*cdf0e10cSrcweir             rBuf.setCharAt(ix,(sal_Unicode)0x0022);
433*cdf0e10cSrcweir         if ((c == 0x2018) || (c == 0x2019))
434*cdf0e10cSrcweir             rBuf.setCharAt(ix,(sal_Unicode)0x0027);
435*cdf0e10cSrcweir     }
436*cdf0e10cSrcweir     OUString nWord(rBuf.makeStringAndClear());
437*cdf0e10cSrcweir 
438*cdf0e10cSrcweir 	if (n)
439*cdf0e10cSrcweir 	{
440*cdf0e10cSrcweir         sal_Int16 nLang = LocaleToLanguage( rLocale );
441*cdf0e10cSrcweir 
442*cdf0e10cSrcweir 	    Sequence< OUString > aStr( 0 );
443*cdf0e10cSrcweir 
444*cdf0e10cSrcweir         for (int i =0; i < numdict; i++)
445*cdf0e10cSrcweir         {
446*cdf0e10cSrcweir             pMS = NULL;
447*cdf0e10cSrcweir             eEnc = RTL_TEXTENCODING_DONTKNOW;
448*cdf0e10cSrcweir             count = 0;
449*cdf0e10cSrcweir 
450*cdf0e10cSrcweir             if (rLocale == aDLocs[i])
451*cdf0e10cSrcweir             {
452*cdf0e10cSrcweir                 pMS = aDicts[i];
453*cdf0e10cSrcweir                 eEnc = aDEncs[i];
454*cdf0e10cSrcweir             }
455*cdf0e10cSrcweir 
456*cdf0e10cSrcweir 	        if (pMS)
457*cdf0e10cSrcweir 	        {
458*cdf0e10cSrcweir                 char ** suglst = NULL;
459*cdf0e10cSrcweir                 OString aWrd(OU2ENC(nWord,eEnc));
460*cdf0e10cSrcweir                 count = pMS->suggest(&suglst, (const char *) aWrd.getStr());
461*cdf0e10cSrcweir 
462*cdf0e10cSrcweir                 if (count)
463*cdf0e10cSrcweir                 {
464*cdf0e10cSrcweir                     aStr.realloc( numsug + count );
465*cdf0e10cSrcweir                     OUString *pStr = aStr.getArray();
466*cdf0e10cSrcweir                     for (int ii=0; ii < count; ++ii)
467*cdf0e10cSrcweir                     {
468*cdf0e10cSrcweir                         OUString cvtwrd(suglst[ii],strlen(suglst[ii]),eEnc);
469*cdf0e10cSrcweir                         pStr[numsug + ii] = cvtwrd;
470*cdf0e10cSrcweir                     }
471*cdf0e10cSrcweir                     pMS->free_list(&suglst, count);
472*cdf0e10cSrcweir                     numsug += count;
473*cdf0e10cSrcweir                 }
474*cdf0e10cSrcweir             }
475*cdf0e10cSrcweir 	    }
476*cdf0e10cSrcweir 
477*cdf0e10cSrcweir         // now return an empty alternative for no suggestions or the list of alternatives if some found
478*cdf0e10cSrcweir 	    SpellAlternatives *pAlt = new SpellAlternatives;
479*cdf0e10cSrcweir         String aTmp(rWord);
480*cdf0e10cSrcweir 	    pAlt->SetWordLanguage( aTmp, nLang );
481*cdf0e10cSrcweir 	    pAlt->SetFailureType( SpellFailure::SPELLING_ERROR );
482*cdf0e10cSrcweir 	    pAlt->SetAlternatives( aStr );
483*cdf0e10cSrcweir 	    xRes = pAlt;
484*cdf0e10cSrcweir         return xRes;
485*cdf0e10cSrcweir 	}
486*cdf0e10cSrcweir     return xRes;
487*cdf0e10cSrcweir }
488*cdf0e10cSrcweir 
489*cdf0e10cSrcweir 
490*cdf0e10cSrcweir Reference< XSpellAlternatives > SAL_CALL SpellChecker::spell(
491*cdf0e10cSrcweir         const OUString& rWord, const Locale& rLocale,
492*cdf0e10cSrcweir         const PropertyValues& rProperties )
493*cdf0e10cSrcweir 		throw(IllegalArgumentException, RuntimeException)
494*cdf0e10cSrcweir {
495*cdf0e10cSrcweir 	MutexGuard	aGuard( GetLinguMutex() );
496*cdf0e10cSrcweir 
497*cdf0e10cSrcweir  	if (rLocale == Locale()  ||  !rWord.getLength())
498*cdf0e10cSrcweir 		return NULL;
499*cdf0e10cSrcweir 
500*cdf0e10cSrcweir 	if (!hasLocale( rLocale ))
501*cdf0e10cSrcweir #ifdef LINGU_EXCEPTIONS
502*cdf0e10cSrcweir 		throw( IllegalArgumentException() );
503*cdf0e10cSrcweir #else
504*cdf0e10cSrcweir 		return NULL;
505*cdf0e10cSrcweir #endif
506*cdf0e10cSrcweir 
507*cdf0e10cSrcweir 	Reference< XSpellAlternatives > xAlt;
508*cdf0e10cSrcweir 	if (!isValid( rWord, rLocale, rProperties ))
509*cdf0e10cSrcweir 	{
510*cdf0e10cSrcweir 		xAlt =  GetProposals( rWord, rLocale );
511*cdf0e10cSrcweir 	}
512*cdf0e10cSrcweir 	return xAlt;
513*cdf0e10cSrcweir }
514*cdf0e10cSrcweir 
515*cdf0e10cSrcweir 
516*cdf0e10cSrcweir Reference< XInterface > SAL_CALL SpellChecker_CreateInstance(
517*cdf0e10cSrcweir         const Reference< XMultiServiceFactory > & /*rSMgr*/ )
518*cdf0e10cSrcweir         throw(Exception)
519*cdf0e10cSrcweir {
520*cdf0e10cSrcweir 
521*cdf0e10cSrcweir 	Reference< XInterface > xService = (cppu::OWeakObject*) new SpellChecker;
522*cdf0e10cSrcweir 	return xService;
523*cdf0e10cSrcweir }
524*cdf0e10cSrcweir 
525*cdf0e10cSrcweir 
526*cdf0e10cSrcweir sal_Bool SAL_CALL SpellChecker::addLinguServiceEventListener(
527*cdf0e10cSrcweir         const Reference< XLinguServiceEventListener >& rxLstnr )
528*cdf0e10cSrcweir         throw(RuntimeException)
529*cdf0e10cSrcweir {
530*cdf0e10cSrcweir 	MutexGuard	aGuard( GetLinguMutex() );
531*cdf0e10cSrcweir 
532*cdf0e10cSrcweir 	sal_Bool bRes = sal_False;
533*cdf0e10cSrcweir 	if (!bDisposing && rxLstnr.is())
534*cdf0e10cSrcweir 	{
535*cdf0e10cSrcweir 		bRes = GetPropHelper().addLinguServiceEventListener( rxLstnr );
536*cdf0e10cSrcweir 	}
537*cdf0e10cSrcweir 	return bRes;
538*cdf0e10cSrcweir }
539*cdf0e10cSrcweir 
540*cdf0e10cSrcweir 
541*cdf0e10cSrcweir sal_Bool SAL_CALL SpellChecker::removeLinguServiceEventListener(
542*cdf0e10cSrcweir         const Reference< XLinguServiceEventListener >& rxLstnr )
543*cdf0e10cSrcweir         throw(RuntimeException)
544*cdf0e10cSrcweir {
545*cdf0e10cSrcweir 	MutexGuard	aGuard( GetLinguMutex() );
546*cdf0e10cSrcweir 
547*cdf0e10cSrcweir 	sal_Bool bRes = sal_False;
548*cdf0e10cSrcweir 	if (!bDisposing && rxLstnr.is())
549*cdf0e10cSrcweir 	{
550*cdf0e10cSrcweir 		DBG_ASSERT( xPropHelper.is(), "xPropHelper non existent" );
551*cdf0e10cSrcweir 		bRes = GetPropHelper().removeLinguServiceEventListener( rxLstnr );
552*cdf0e10cSrcweir 	}
553*cdf0e10cSrcweir 	return bRes;
554*cdf0e10cSrcweir }
555*cdf0e10cSrcweir 
556*cdf0e10cSrcweir 
557*cdf0e10cSrcweir OUString SAL_CALL SpellChecker::getServiceDisplayName( const Locale& /*rLocale*/ )
558*cdf0e10cSrcweir 		throw(RuntimeException)
559*cdf0e10cSrcweir {
560*cdf0e10cSrcweir 	MutexGuard	aGuard( GetLinguMutex() );
561*cdf0e10cSrcweir 	return A2OU( "Hunspell SpellChecker" );
562*cdf0e10cSrcweir }
563*cdf0e10cSrcweir 
564*cdf0e10cSrcweir 
565*cdf0e10cSrcweir void SAL_CALL SpellChecker::initialize( const Sequence< Any >& rArguments )
566*cdf0e10cSrcweir 		throw(Exception, RuntimeException)
567*cdf0e10cSrcweir {
568*cdf0e10cSrcweir 	MutexGuard	aGuard( GetLinguMutex() );
569*cdf0e10cSrcweir 
570*cdf0e10cSrcweir 	if (!pPropHelper)
571*cdf0e10cSrcweir 	{
572*cdf0e10cSrcweir 		sal_Int32 nLen = rArguments.getLength();
573*cdf0e10cSrcweir 		if (2 == nLen)
574*cdf0e10cSrcweir 		{
575*cdf0e10cSrcweir 			Reference< XPropertySet	>	xPropSet;
576*cdf0e10cSrcweir 			rArguments.getConstArray()[0] >>= xPropSet;
577*cdf0e10cSrcweir 			//rArguments.getConstArray()[1] >>= xDicList;
578*cdf0e10cSrcweir 
579*cdf0e10cSrcweir 			//! Pointer allows for access of the non-UNO functions.
580*cdf0e10cSrcweir 			//! And the reference to the UNO-functions while increasing
581*cdf0e10cSrcweir 			//! the ref-count and will implicitly free the memory
582*cdf0e10cSrcweir 			//! when the object is not longer used.
583*cdf0e10cSrcweir 			pPropHelper = new PropertyHelper_Spell( (XSpellChecker *) this, xPropSet );
584*cdf0e10cSrcweir 			xPropHelper = pPropHelper;
585*cdf0e10cSrcweir 			pPropHelper->AddAsPropListener();	//! after a reference is established
586*cdf0e10cSrcweir 		}
587*cdf0e10cSrcweir         else
588*cdf0e10cSrcweir         {
589*cdf0e10cSrcweir 			DBG_ERROR( "wrong number of arguments in sequence" );
590*cdf0e10cSrcweir         }
591*cdf0e10cSrcweir 	}
592*cdf0e10cSrcweir }
593*cdf0e10cSrcweir 
594*cdf0e10cSrcweir 
595*cdf0e10cSrcweir void SAL_CALL SpellChecker::dispose()
596*cdf0e10cSrcweir 		throw(RuntimeException)
597*cdf0e10cSrcweir {
598*cdf0e10cSrcweir 	MutexGuard	aGuard( GetLinguMutex() );
599*cdf0e10cSrcweir 
600*cdf0e10cSrcweir 	if (!bDisposing)
601*cdf0e10cSrcweir 	{
602*cdf0e10cSrcweir 		bDisposing = sal_True;
603*cdf0e10cSrcweir 		EventObject	aEvtObj( (XSpellChecker *) this );
604*cdf0e10cSrcweir 		aEvtListeners.disposeAndClear( aEvtObj );
605*cdf0e10cSrcweir 	}
606*cdf0e10cSrcweir }
607*cdf0e10cSrcweir 
608*cdf0e10cSrcweir 
609*cdf0e10cSrcweir void SAL_CALL SpellChecker::addEventListener( const Reference< XEventListener >& rxListener )
610*cdf0e10cSrcweir 		throw(RuntimeException)
611*cdf0e10cSrcweir {
612*cdf0e10cSrcweir 	MutexGuard	aGuard( GetLinguMutex() );
613*cdf0e10cSrcweir 
614*cdf0e10cSrcweir 	if (!bDisposing && rxListener.is())
615*cdf0e10cSrcweir 		aEvtListeners.addInterface( rxListener );
616*cdf0e10cSrcweir }
617*cdf0e10cSrcweir 
618*cdf0e10cSrcweir 
619*cdf0e10cSrcweir void SAL_CALL SpellChecker::removeEventListener( const Reference< XEventListener >& rxListener )
620*cdf0e10cSrcweir 		throw(RuntimeException)
621*cdf0e10cSrcweir {
622*cdf0e10cSrcweir 	MutexGuard	aGuard( GetLinguMutex() );
623*cdf0e10cSrcweir 
624*cdf0e10cSrcweir 	if (!bDisposing && rxListener.is())
625*cdf0e10cSrcweir 		aEvtListeners.removeInterface( rxListener );
626*cdf0e10cSrcweir }
627*cdf0e10cSrcweir 
628*cdf0e10cSrcweir 
629*cdf0e10cSrcweir ///////////////////////////////////////////////////////////////////////////
630*cdf0e10cSrcweir // Service specific part
631*cdf0e10cSrcweir //
632*cdf0e10cSrcweir 
633*cdf0e10cSrcweir OUString SAL_CALL SpellChecker::getImplementationName()
634*cdf0e10cSrcweir 		throw(RuntimeException)
635*cdf0e10cSrcweir {
636*cdf0e10cSrcweir 	MutexGuard	aGuard( GetLinguMutex() );
637*cdf0e10cSrcweir 
638*cdf0e10cSrcweir 	return getImplementationName_Static();
639*cdf0e10cSrcweir }
640*cdf0e10cSrcweir 
641*cdf0e10cSrcweir 
642*cdf0e10cSrcweir sal_Bool SAL_CALL SpellChecker::supportsService( const OUString& ServiceName )
643*cdf0e10cSrcweir 		throw(RuntimeException)
644*cdf0e10cSrcweir {
645*cdf0e10cSrcweir 	MutexGuard	aGuard( GetLinguMutex() );
646*cdf0e10cSrcweir 
647*cdf0e10cSrcweir 	Sequence< OUString > aSNL = getSupportedServiceNames();
648*cdf0e10cSrcweir 	const OUString * pArray = aSNL.getConstArray();
649*cdf0e10cSrcweir 	for( sal_Int32 i = 0; i < aSNL.getLength(); i++ )
650*cdf0e10cSrcweir 		if( pArray[i] == ServiceName )
651*cdf0e10cSrcweir 			return sal_True;
652*cdf0e10cSrcweir 	return sal_False;
653*cdf0e10cSrcweir }
654*cdf0e10cSrcweir 
655*cdf0e10cSrcweir 
656*cdf0e10cSrcweir Sequence< OUString > SAL_CALL SpellChecker::getSupportedServiceNames()
657*cdf0e10cSrcweir 		throw(RuntimeException)
658*cdf0e10cSrcweir {
659*cdf0e10cSrcweir 	MutexGuard	aGuard( GetLinguMutex() );
660*cdf0e10cSrcweir 
661*cdf0e10cSrcweir 	return getSupportedServiceNames_Static();
662*cdf0e10cSrcweir }
663*cdf0e10cSrcweir 
664*cdf0e10cSrcweir 
665*cdf0e10cSrcweir Sequence< OUString > SpellChecker::getSupportedServiceNames_Static()
666*cdf0e10cSrcweir 		throw()
667*cdf0e10cSrcweir {
668*cdf0e10cSrcweir 	MutexGuard	aGuard( GetLinguMutex() );
669*cdf0e10cSrcweir 
670*cdf0e10cSrcweir 	Sequence< OUString > aSNS( 1 );	// auch mehr als 1 Service moeglich
671*cdf0e10cSrcweir 	aSNS.getArray()[0] = A2OU( SN_SPELLCHECKER );
672*cdf0e10cSrcweir 	return aSNS;
673*cdf0e10cSrcweir }
674*cdf0e10cSrcweir 
675*cdf0e10cSrcweir void * SAL_CALL SpellChecker_getFactory( const sal_Char * pImplName,
676*cdf0e10cSrcweir 			XMultiServiceFactory * pServiceManager, void *  )
677*cdf0e10cSrcweir {
678*cdf0e10cSrcweir 	void * pRet = 0;
679*cdf0e10cSrcweir 	if ( !SpellChecker::getImplementationName_Static().compareToAscii( pImplName ) )
680*cdf0e10cSrcweir 	{
681*cdf0e10cSrcweir 		Reference< XSingleServiceFactory > xFactory =
682*cdf0e10cSrcweir 			cppu::createOneInstanceFactory(
683*cdf0e10cSrcweir 				pServiceManager,
684*cdf0e10cSrcweir 				SpellChecker::getImplementationName_Static(),
685*cdf0e10cSrcweir 				SpellChecker_CreateInstance,
686*cdf0e10cSrcweir 				SpellChecker::getSupportedServiceNames_Static());
687*cdf0e10cSrcweir 		// acquire, because we return an interface pointer instead of a reference
688*cdf0e10cSrcweir 		xFactory->acquire();
689*cdf0e10cSrcweir 		pRet = xFactory.get();
690*cdf0e10cSrcweir 	}
691*cdf0e10cSrcweir 	return pRet;
692*cdf0e10cSrcweir }
693*cdf0e10cSrcweir 
694*cdf0e10cSrcweir 
695*cdf0e10cSrcweir ///////////////////////////////////////////////////////////////////////////
696