1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_lingucomponent.hxx"
26 
27 
28 #include <com/sun/star/uno/Reference.h>
29 #include <com/sun/star/linguistic2/XSearchableDictionaryList.hpp>
30 
31 #include <cppuhelper/factory.hxx>	// helper for factories
32 #include <com/sun/star/registry/XRegistryKey.hpp>
33 #include <i18npool/mslangid.hxx>
34 #include <unotools/pathoptions.hxx>
35 #include <unotools/useroptions.hxx>
36 #include <tools/debug.hxx>
37 #include <unotools/processfactory.hxx>
38 #include <osl/mutex.hxx>
39 
40 #include <hyphen.h>
41 #include <hyphenimp.hxx>
42 
43 #include <linguistic/hyphdta.hxx>
44 #include <rtl/ustring.hxx>
45 #include <rtl/ustrbuf.hxx>
46 #include <rtl/textenc.h>
47 
48 #include <linguistic/lngprops.hxx>
49 #include <unotools/pathoptions.hxx>
50 #include <unotools/useroptions.hxx>
51 #include <unotools/lingucfg.hxx>
52 #include <osl/file.hxx>
53 
54 #include <stdio.h>
55 #include <string.h>
56 
57 #include <list>
58 #include <set>
59 
60 using namespace utl;
61 using namespace osl;
62 using namespace rtl;
63 using namespace com::sun::star;
64 using namespace com::sun::star::beans;
65 using namespace com::sun::star::lang;
66 using namespace com::sun::star::uno;
67 using namespace com::sun::star::linguistic2;
68 using namespace linguistic;
69 
70 // values asigned to capitalization types
71 #define CAPTYPE_UNKNOWN 0
72 #define CAPTYPE_NOCAP   1
73 #define CAPTYPE_INITCAP 2
74 #define CAPTYPE_ALLCAP  3
75 #define CAPTYPE_MIXED   4
76 
77 // min, max
78 
79 //#define Min(a,b) (a < b ? a : b)
80 #define Max(a,b) (a > b ? a : b)
81 
82 ///////////////////////////////////////////////////////////////////////////
83 
84 
85 Hyphenator::Hyphenator() :
86 	aEvtListeners	( GetLinguMutex() )
87 {
88 	bDisposing = sal_False;
89 	pPropHelper = NULL;
90     aDicts = NULL;
91     numdict = 0;
92 }
93 
94 
95 Hyphenator::~Hyphenator()
96 {
97 	if (pPropHelper)
98 		pPropHelper->RemoveAsPropListener();
99 
100     if ((numdict) && (aDicts))
101     {
102         for (int i=0; i < numdict; i++)
103         {
104             if (aDicts[i].apCC) delete aDicts[i].apCC;
105             aDicts[i].apCC = NULL;
106         }
107 	}
108     if (aDicts) delete[] aDicts;
109 	aDicts = NULL;
110     numdict = 0;
111 }
112 
113 
114 PropertyHelper_Hyphen & Hyphenator::GetPropHelper_Impl()
115 {
116 	if (!pPropHelper)
117 	{
118 		Reference< XPropertySet	>	xPropSet( GetLinguProperties(), UNO_QUERY );
119 
120 		pPropHelper	= new PropertyHelper_Hyphen ((XHyphenator *) this, xPropSet );
121 		xPropHelper = pPropHelper;
122 		pPropHelper->AddAsPropListener();	//! after a reference is established
123 	}
124 	return *pPropHelper;
125 
126 }
127 
128 
129 Sequence< Locale > SAL_CALL Hyphenator::getLocales()
130 		throw(RuntimeException)
131 {
132 	MutexGuard	aGuard( GetLinguMutex() );
133 
134     // this routine should return the locales supported by the installed
135     // dictionaries.
136 
137     if (!numdict)
138     {
139         SvtLinguConfig aLinguCfg;
140 
141         // get list of dictionaries-to-use
142 		// (or better speaking: the list of dictionaries using the
143 		// new configuration entries).
144         std::list< SvtLinguConfigDictionaryEntry > aDics;
145         uno::Sequence< rtl::OUString > aFormatList;
146         aLinguCfg.GetSupportedDictionaryFormatsFor( A2OU("Hyphenators"),
147                 A2OU("org.openoffice.lingu.LibHnjHyphenator"), aFormatList );
148         sal_Int32 nLen = aFormatList.getLength();
149         for (sal_Int32 i = 0;  i < nLen;  ++i)
150         {
151             std::vector< SvtLinguConfigDictionaryEntry > aTmpDic(
152                     aLinguCfg.GetActiveDictionariesByFormat( aFormatList[i] ) );
153             aDics.insert( aDics.end(), aTmpDic.begin(), aTmpDic.end() );
154         }
155 
156         //!! for compatibility with old dictionaries (the ones not using extensions
157         //!! or new configuration entries, but still using the dictionary.lst file)
158 		//!! Get the list of old style spell checking dictionaries to use...
159         std::vector< SvtLinguConfigDictionaryEntry > aOldStyleDics(
160 				GetOldStyleDics( "HYPH" ) );
161 
162 		// to prefer dictionaries with configuration entries we will only
163 		// use those old style dictionaries that add a language that
164 		// is not yet supported by the list od new style dictionaries
165 		MergeNewStyleDicsAndOldStyleDics( aDics, aOldStyleDics );
166 
167         numdict = aDics.size();
168         if (numdict)
169         {
170             // get supported locales from the dictionaries-to-use...
171             sal_Int32 k = 0;
172             std::set< rtl::OUString, lt_rtl_OUString > aLocaleNamesSet;
173             std::list< SvtLinguConfigDictionaryEntry >::const_iterator aDictIt;
174             for (aDictIt = aDics.begin();  aDictIt != aDics.end();  ++aDictIt)
175             {
176                 uno::Sequence< rtl::OUString > aLocaleNames( aDictIt->aLocaleNames );
177                 sal_Int32 nLen2 = aLocaleNames.getLength();
178                 for (k = 0;  k < nLen2;  ++k)
179                 {
180                     aLocaleNamesSet.insert( aLocaleNames[k] );
181                 }
182             }
183             // ... and add them to the resulting sequence
184             aSuppLocales.realloc( aLocaleNamesSet.size() );
185             std::set< rtl::OUString, lt_rtl_OUString >::const_iterator aItB;
186             k = 0;
187             for (aItB = aLocaleNamesSet.begin();  aItB != aLocaleNamesSet.end();  ++aItB)
188             {
189                 Locale aTmp( MsLangId::convertLanguageToLocale(
190                         MsLangId::convertIsoStringToLanguage( *aItB )));
191                 aSuppLocales[k++] = aTmp;
192             }
193 
194             //! For each dictionary and each locale we need a seperate entry.
195             //! If this results in more than one dictionary per locale than (for now)
196 			//! it is undefined which dictionary gets used.
197 			//! In the future the implementation should support using several dictionaries
198 			//! for one locale.
199 			numdict = 0;
200             for (aDictIt = aDics.begin();  aDictIt != aDics.end();  ++aDictIt)
201 				numdict = numdict + aDictIt->aLocaleNames.getLength();
202 
203             // add dictionary information
204             aDicts = new HDInfo[numdict];
205 
206             k = 0;
207             for (aDictIt = aDics.begin();  aDictIt != aDics.end();  ++aDictIt)
208             {
209                 if (aDictIt->aLocaleNames.getLength() > 0 &&
210                     aDictIt->aLocations.getLength() > 0)
211                 {
212                     uno::Sequence< rtl::OUString > aLocaleNames( aDictIt->aLocaleNames );
213                     sal_Int32 nLocales = aLocaleNames.getLength();
214 
215                     // currently only one language per dictionary is supported in the actual implementation...
216                     // Thus here we work-around this by adding the same dictionary several times.
217                     // Once for each of it's supported locales.
218                     for (sal_Int32 i = 0;  i < nLocales;  ++i)
219                     {
220                         aDicts[k].aPtr = NULL;
221                         aDicts[k].eEnc = RTL_TEXTENCODING_DONTKNOW;
222                         aDicts[k].aLoc = MsLangId::convertLanguageToLocale(
223                                         MsLangId::convertIsoStringToLanguage( aDictIt->aLocaleNames[i] ));
224                         aDicts[k].apCC = new CharClass( aDicts[k].aLoc );
225                         // also both files have to be in the same directory and the
226                         // file names must only differ in the extension (.aff/.dic).
227                         // Thus we use the first location only and strip the extension part.
228                         rtl::OUString aLocation = aDictIt->aLocations[0];
229                         sal_Int32 nPos = aLocation.lastIndexOf( '.' );
230                         aLocation = aLocation.copy( 0, nPos );
231                         aDicts[k].aName = aLocation;
232 
233                         ++k;
234                     }
235                 }
236             }
237             DBG_ASSERT( k == numdict, "index mismatch?" );
238         }
239         else
240         {
241             /* no dictionary found so register no dictionaries */
242             numdict = 0;
243             aDicts = NULL;
244             aSuppLocales.realloc(0);
245         }
246     }
247 
248     return aSuppLocales;
249 }
250 
251 
252 
253 sal_Bool SAL_CALL Hyphenator::hasLocale(const Locale& rLocale)
254 		throw(RuntimeException)
255 {
256 	MutexGuard	aGuard( GetLinguMutex() );
257 
258 	sal_Bool bRes = sal_False;
259 	if (!aSuppLocales.getLength())
260 		getLocales();
261 
262 	const Locale *pLocale = aSuppLocales.getConstArray();
263 	sal_Int32 nLen = aSuppLocales.getLength();
264 	for (sal_Int32 i = 0;  i < nLen;  ++i)
265 	{
266 		if (rLocale == pLocale[i])
267 		{
268 			bRes = sal_True;
269 			break;
270 		}
271 	}
272 	return bRes;
273 }
274 
275 
276 Reference< XHyphenatedWord > SAL_CALL Hyphenator::hyphenate( const ::rtl::OUString& aWord,
277        const ::com::sun::star::lang::Locale& aLocale,
278        sal_Int16 nMaxLeading,
279        const ::com::sun::star::beans::PropertyValues& aProperties )
280        throw (com::sun::star::uno::RuntimeException, com::sun::star::lang::IllegalArgumentException)
281 {
282 	int nHyphenationPos = -1;
283     int nHyphenationPosAlt = -1;
284     int nHyphenationPosAltHyph = -1;
285 	int wordlen;
286 	char *hyphens;
287     char *lcword;
288     int k = 0;
289 
290     PropertyHelper_Hyphen & rHelper = GetPropHelper();
291     rHelper.SetTmpPropVals(aProperties);
292 	sal_Int16 minTrail = rHelper.GetMinTrailing();
293 	sal_Int16 minLead = rHelper.GetMinLeading();
294 	sal_Int16 minLen = rHelper.GetMinWordLength();
295 
296 	HyphenDict *dict = NULL;
297     rtl_TextEncoding eEnc = RTL_TEXTENCODING_DONTKNOW;
298     CharClass * pCC = NULL;
299 
300 	Reference< XHyphenatedWord > xRes;
301 
302     k = -1;
303     for (int j = 0; j < numdict; j++)
304     {
305         if (aLocale == aDicts[j].aLoc)
306             k = j;
307     }
308 
309     // if we have a hyphenation dictionary matching this locale
310     if (k != -1)
311     {
312         // if this dictinary has not been loaded yet do that
313         if (!aDicts[k].aPtr)
314         {
315             OUString DictFN = aDicts[k].aName + A2OU(".dic");
316             OUString dictpath;
317 
318             osl::FileBase::getSystemPathFromFileURL( DictFN, dictpath );
319             OString sTmp( OU2ENC( dictpath, osl_getThreadTextEncoding() ) );
320 
321 #if defined(WNT)
322             // workaround for Windows specifc problem that the
323             // path length in calls to 'fopen' is limted to somewhat
324             // about 120+ characters which will usually be exceed when
325             // using dictionaries as extensions.
326             sTmp = Win_GetShortPathName( dictpath );
327 #endif
328 
329             if ( ( dict = hnj_hyphen_load ( sTmp.getStr()) ) == NULL )
330             {
331                fprintf(stderr, "Couldn't find file %s\n", OU2ENC(dictpath, osl_getThreadTextEncoding()) );
332                return NULL;
333             }
334             aDicts[k].aPtr = dict;
335             aDicts[k].eEnc = getTextEncodingFromCharset(dict->cset);
336         }
337 
338         // other wise hyphenate the word with that dictionary
339         dict = aDicts[k].aPtr;
340         eEnc = aDicts[k].eEnc;
341         pCC =  aDicts[k].apCC;
342 
343         // we don't want to work with a default text encoding since following incorrect
344         // results may occur only for specific text and thus may be hard to notice.
345         // Thus better always make a clean exit here if the text encoding is in question.
346         // Hopefully something not working at all will raise proper attention quickly. ;-)
347         DBG_ASSERT( eEnc != RTL_TEXTENCODING_DONTKNOW, "failed to get text encoding! (maybe incorrect encoding string in file)" );
348         if (eEnc == RTL_TEXTENCODING_DONTKNOW)
349             return NULL;
350 
351         sal_uInt16 ct = CAPTYPE_UNKNOWN;
352         ct = capitalType(aWord, pCC);
353 
354         // first convert any smart quotes or apostrophes to normal ones
355 	    OUStringBuffer rBuf(aWord);
356         sal_Int32 nc = rBuf.getLength();
357         sal_Unicode ch;
358         for (sal_Int32 ix=0; ix < nc; ix++)
359         {
360 	        ch = rBuf.charAt(ix);
361             if ((ch == 0x201C) || (ch == 0x201D))
362                 rBuf.setCharAt(ix,(sal_Unicode)0x0022);
363             if ((ch == 0x2018) || (ch == 0x2019))
364                 rBuf.setCharAt(ix,(sal_Unicode)0x0027);
365         }
366         OUString nWord(rBuf.makeStringAndClear());
367 
368         // now convert word to all lowercase for pattern recognition
369         OUString nTerm(makeLowerCase(nWord, pCC));
370 
371         // now convert word to needed encoding
372         OString encWord(OU2ENC(nTerm,eEnc));
373 
374 	    wordlen = encWord.getLength();
375         lcword = new char[wordlen + 1];
376 	    hyphens = new char[wordlen + 5];
377 
378         char ** rep = NULL; // replacements of discretionary hyphenation
379         int * pos = NULL; // array of [hyphenation point] minus [deletion position]
380         int * cut = NULL; // length of deletions in original word
381 
382         // copy converted word into simple char buffer
383         strcpy(lcword,encWord.getStr());
384 
385         // now strip off any ending periods
386         int n = wordlen-1;
387         while((n >=0) && (lcword[n] == '.'))
388             n--;
389         n++;
390         if (n > 0)
391         {
392             const bool bFailed = 0 != hnj_hyphen_hyphenate3( dict, lcword, n, hyphens, NULL,
393                     &rep, &pos, &cut, minLead, minTrail,
394                     Max(dict->clhmin, Max(dict->clhmin, 2) + Max(0, minLead  - Max(dict->lhmin, 2))),
395                     Max(dict->crhmin, Max(dict->crhmin, 2) + Max(0, minTrail - Max(dict->rhmin, 2))) );
396             if (bFailed)
397             {
398                 //whoops something did not work
399                 delete[] hyphens;
400                 delete[] lcword;
401                 if (rep)
402                 {
403                     for(int j = 0; j < n; j++)
404                     {
405                         if (rep[j]) free(rep[j]);
406                     }
407                     free(rep);
408                 }
409                 if (pos) free(pos);
410                 if (cut) free(cut);
411                 return NULL;
412             }
413         }
414 
415         // now backfill hyphens[] for any removed trailing periods
416         for (int c = n; c < wordlen; c++) hyphens[c] = '0';
417         hyphens[wordlen] = '\0';
418 
419 	    sal_Int32 Leading =  GetPosInWordToCheck( aWord, nMaxLeading );
420 
421 	    for (sal_Int32 i = 0; i < n; i++)
422 	    {
423             int leftrep = 0;
424             sal_Bool hit = (n >= minLen);
425             if (!rep || !rep[i] || (i >= n))
426             {
427                 hit = hit && (hyphens[i]&1) && (i < Leading);
428                 hit = hit && (i >= (minLead-1) );
429                 hit = hit && ((n - i - 1) >= minTrail);
430             }
431             else
432             {
433                 // calculate change character length before hyphenation point signed with '='
434                 for (char * c = rep[i]; *c && (*c != '='); c++)
435                 {
436                     if (eEnc == RTL_TEXTENCODING_UTF8)
437                     {
438                         if (((unsigned char) *c) >> 6 != 2)
439                             leftrep++;
440                     }
441                     else
442                         leftrep++;
443                 }
444                 hit = hit && (hyphens[i]&1) && ((i + leftrep - pos[i]) < Leading);
445                 hit = hit && ((i + leftrep - pos[i]) >= (minLead-1) );
446                 hit = hit && ((n - i - 1 + sal::static_int_cast< sal_sSize >(strlen(rep[i])) - leftrep - 1) >= minTrail);
447             }
448             if (hit)
449             {
450                 nHyphenationPos = i;
451                 if (rep && (i < n) && rep[i])
452                 {
453                     nHyphenationPosAlt = i - pos[i];
454                     nHyphenationPosAltHyph = i + leftrep - pos[i];
455                 }
456             }
457         }
458 
459         if (nHyphenationPos  == -1)
460         {
461             xRes = NULL;
462         }
463         else
464         {
465             if (rep && rep[nHyphenationPos])
466             {
467                 // remove equal sign
468                 char * s = rep[nHyphenationPos];
469                 int eq = 0;
470                 for (; *s; s++)
471                 {
472                     if (*s == '=') eq = 1;
473                     if (eq) *s = *(s + 1);
474                 }
475                 OUString repHyphlow(rep[nHyphenationPos], strlen(rep[nHyphenationPos]), eEnc);
476                 OUString repHyph;
477                 switch (ct)
478                 {
479                     case CAPTYPE_ALLCAP:
480                     {
481                         repHyph = makeUpperCase(repHyphlow, pCC);
482                         break;
483                     }
484                     case CAPTYPE_INITCAP:
485                     {
486                         if (nHyphenationPosAlt == 0)
487                             repHyph = makeInitCap(repHyphlow, pCC);
488                         else
489                              repHyph = repHyphlow;
490                         break;
491                     }
492                     default:
493                     {
494                         repHyph = repHyphlow;
495                         break;
496                     }
497                 }
498 
499                 // handle shortening
500                 sal_Int16 nPos = (sal_Int16) ((nHyphenationPosAltHyph < nHyphenationPos) ?
501                 nHyphenationPosAltHyph : nHyphenationPos);
502                 // dicretionary hyphenation
503                 xRes = new HyphenatedWord( aWord, LocaleToLanguage( aLocale ), nPos,
504                     aWord.replaceAt(nHyphenationPosAlt + 1, cut[nHyphenationPos], repHyph),
505                     (sal_Int16) nHyphenationPosAltHyph);
506             }
507             else
508             {
509                 xRes = new HyphenatedWord( aWord, LocaleToLanguage( aLocale ),
510                     (sal_Int16)nHyphenationPos, aWord, (sal_Int16) nHyphenationPos);
511             }
512         }
513 
514         delete[] lcword;
515 	    delete[] hyphens;
516         if (rep)
517         {
518             for(int j = 0; j < n; j++)
519             {
520                 if (rep[j]) free(rep[j]);
521             }
522             free(rep);
523         }
524         if (pos) free(pos);
525         if (cut) free(cut);
526 	    return xRes;
527 	}
528     return NULL;
529 }
530 
531 
532 Reference < XHyphenatedWord > SAL_CALL Hyphenator::queryAlternativeSpelling(
533         const ::rtl::OUString& /*aWord*/,
534         const ::com::sun::star::lang::Locale& /*aLocale*/,
535         sal_Int16 /*nIndex*/,
536         const ::com::sun::star::beans::PropertyValues& /*aProperties*/ )
537         throw(::com::sun::star::lang::IllegalArgumentException, ::com::sun::star::uno::RuntimeException)
538 {
539   /* alternative spelling isn't supported by tex dictionaries */
540   /* XXX: OOo's extended libhjn algorithm can support alternative spellings with extended TeX dic. */
541   /* TASK: implement queryAlternativeSpelling() */
542   return NULL;
543 }
544 
545 Reference< XPossibleHyphens > SAL_CALL Hyphenator::createPossibleHyphens( const ::rtl::OUString& aWord,
546         const ::com::sun::star::lang::Locale& aLocale,
547         const ::com::sun::star::beans::PropertyValues& aProperties )
548         throw(::com::sun::star::lang::IllegalArgumentException, ::com::sun::star::uno::RuntimeException)
549 {
550     int wordlen;
551     char *hyphens;
552     char *lcword;
553     int k;
554 
555     PropertyHelper_Hyphen & rHelper = GetPropHelper();
556     rHelper.SetTmpPropVals(aProperties);
557     sal_Int16 minTrail = rHelper.GetMinTrailing();
558     sal_Int16 minLead = rHelper.GetMinLeading();
559 
560     HyphenDict *dict = NULL;
561     rtl_TextEncoding eEnc = RTL_TEXTENCODING_DONTKNOW;
562     CharClass* pCC = NULL;
563 
564     Reference< XPossibleHyphens > xRes;
565 
566     k = -1;
567     for (int j = 0; j < numdict; j++)
568     {
569         if (aLocale == aDicts[j].aLoc) k = j;
570     }
571 
572     // if we have a hyphenation dictionary matching this locale
573     if (k != -1)
574     {
575         // if this dictioanry has not been loaded yet do that
576         if (!aDicts[k].aPtr)
577         {
578             OUString DictFN = aDicts[k].aName + A2OU(".dic");
579             OUString dictpath;
580 
581             osl::FileBase::getSystemPathFromFileURL( DictFN, dictpath );
582             OString sTmp( OU2ENC( dictpath, osl_getThreadTextEncoding() ) );
583 
584 #if defined(WNT)
585             // workaround for Windows specifc problem that the
586             // path length in calls to 'fopen' is limted to somewhat
587             // about 120+ characters which will usually be exceed when
588             // using dictionaries as extensions.
589             sTmp = Win_GetShortPathName( dictpath );
590 #endif
591 
592             if ( ( dict = hnj_hyphen_load ( sTmp.getStr()) ) == NULL )
593             {
594                fprintf(stderr, "Couldn't find file %s and %s\n", sTmp.getStr(), OU2ENC(dictpath, osl_getThreadTextEncoding()) );
595                return NULL;
596             }
597             aDicts[k].aPtr = dict;
598             aDicts[k].eEnc = getTextEncodingFromCharset(dict->cset);
599         }
600 
601         // other wise hyphenate the word with that dictionary
602         dict = aDicts[k].aPtr;
603         eEnc = aDicts[k].eEnc;
604         pCC  = aDicts[k].apCC;
605 
606         // we don't want to work with a default text encoding since following incorrect
607         // results may occur only for specific text and thus may be hard to notice.
608         // Thus better always make a clean exit here if the text encoding is in question.
609         // Hopefully something not working at all will raise proper attention quickly. ;-)
610         DBG_ASSERT( eEnc != RTL_TEXTENCODING_DONTKNOW, "failed to get text encoding! (maybe incorrect encoding string in file)" );
611         if (eEnc == RTL_TEXTENCODING_DONTKNOW)
612             return NULL;
613 
614         // first handle smart quotes both single and double
615         OUStringBuffer rBuf(aWord);
616         sal_Int32 nc = rBuf.getLength();
617         sal_Unicode ch;
618         for (sal_Int32 ix=0; ix < nc; ix++)
619         {
620             ch = rBuf.charAt(ix);
621             if ((ch == 0x201C) || (ch == 0x201D))
622                 rBuf.setCharAt(ix,(sal_Unicode)0x0022);
623             if ((ch == 0x2018) || (ch == 0x2019))
624                 rBuf.setCharAt(ix,(sal_Unicode)0x0027);
625         }
626         OUString nWord(rBuf.makeStringAndClear());
627 
628         // now convert word to all lowercase for pattern recognition
629         OUString nTerm(makeLowerCase(nWord, pCC));
630 
631         // now convert word to needed encoding
632         OString encWord(OU2ENC(nTerm,eEnc));
633 
634         wordlen = encWord.getLength();
635         lcword = new char[wordlen+1];
636         hyphens = new char[wordlen+5];
637         char ** rep = NULL; // replacements of discretionary hyphenation
638         int * pos = NULL; // array of [hyphenation point] minus [deletion position]
639         int * cut = NULL; // length of deletions in original word
640 
641         // copy converted word into simple char buffer
642         strcpy(lcword,encWord.getStr());
643 
644         // first remove any trailing periods
645         int n = wordlen-1;
646         while((n >=0) && (lcword[n] == '.'))
647             n--;
648         n++;
649         // fprintf(stderr,"hyphenate... %s\n",lcword); fflush(stderr);
650         if (n > 0)
651         {
652             const bool bFailed = 0 != hnj_hyphen_hyphenate3(dict, lcword, n, hyphens, NULL,
653                     &rep, &pos, &cut, minLead, minTrail,
654                     Max(dict->clhmin, Max(dict->clhmin, 2) + Max(0, minLead - Max(dict->lhmin, 2))),
655                     Max(dict->crhmin, Max(dict->crhmin, 2) + Max(0, minTrail - Max(dict->rhmin, 2))) );
656             if (bFailed)
657             {
658                 delete[] hyphens;
659                 delete[] lcword;
660 
661                 if (rep)
662                 {
663                     for(int j = 0; j < n; j++)
664                     {
665                         if (rep[j]) free(rep[j]);
666                     }
667                     free(rep);
668                 }
669                 if (pos) free(pos);
670                 if (cut) free(cut);
671 
672                 return NULL;
673             }
674         }
675         // now backfill hyphens[] for any removed periods
676         for (int c = n; c < wordlen; c++)
677             hyphens[c] = '0';
678         hyphens[wordlen] = '\0';
679         // fprintf(stderr,"... %s\n",hyphens); fflush(stderr);
680 
681         sal_Int16 nHyphCount = 0;
682         sal_Int16 i;
683 
684         for ( i = 0; i < encWord.getLength(); i++)
685         {
686             if (hyphens[i]&1 && (!rep || !rep[i]))
687                 nHyphCount++;
688         }
689 
690         Sequence< sal_Int16 > aHyphPos(nHyphCount);
691         sal_Int16 *pPos = aHyphPos.getArray();
692         OUStringBuffer hyphenatedWordBuffer;
693         OUString hyphenatedWord;
694         nHyphCount = 0;
695 
696         for (i = 0; i < nWord.getLength(); i++)
697         {
698             hyphenatedWordBuffer.append(aWord[i]);
699             // hyphenation position (not alternative)
700             if (hyphens[i]&1 && (!rep || !rep[i]))
701             {
702                 pPos[nHyphCount] = i;
703                 hyphenatedWordBuffer.append(sal_Unicode('='));
704                 nHyphCount++;
705             }
706         }
707 
708         hyphenatedWord = hyphenatedWordBuffer.makeStringAndClear();
709         //fprintf(stderr,"result is %s\n",OU2A(hyphenatedWord));
710         //fflush(stderr);
711 
712         xRes = new PossibleHyphens( aWord, LocaleToLanguage( aLocale ),
713                   hyphenatedWord, aHyphPos );
714 
715         delete[] hyphens;
716         delete[] lcword;
717 
718         if (rep)
719         {
720             for(int j = 0; j < n; j++)
721             {
722                 if (rep[j]) free(rep[j]);
723             }
724             free(rep);
725         }
726         if (pos) free(pos);
727         if (cut) free(cut);
728 
729         return xRes;
730     }
731 
732     return NULL;
733 }
734 
735 sal_uInt16 SAL_CALL Hyphenator::capitalType(const OUString& aTerm, CharClass * pCC)
736 {
737     sal_Int32 tlen = aTerm.getLength();
738     if ((pCC) && (tlen))
739     {
740         String aStr(aTerm);
741         sal_Int32 nc = 0;
742         for (xub_StrLen tindex = 0; tindex < tlen;  tindex++)
743         {
744             if (pCC->getCharacterType(aStr,tindex) & ::com::sun::star::i18n::KCharacterType::UPPER)
745                 nc++;
746         }
747 
748         if (nc == 0)
749             return (sal_uInt16) CAPTYPE_NOCAP;
750         if (nc == tlen)
751             return (sal_uInt16) CAPTYPE_ALLCAP;
752         if ((nc == 1) && (pCC->getCharacterType(aStr,0) & ::com::sun::star::i18n::KCharacterType::UPPER))
753             return (sal_uInt16) CAPTYPE_INITCAP;
754 
755         return (sal_uInt16) CAPTYPE_MIXED;
756     }
757     return (sal_uInt16) CAPTYPE_UNKNOWN;
758 }
759 
760 OUString SAL_CALL Hyphenator::makeLowerCase(const OUString& aTerm, CharClass * pCC)
761 {
762     if (pCC)
763         return pCC->toLower_rtl(aTerm, 0, aTerm.getLength());
764     return aTerm;
765 }
766 
767 OUString SAL_CALL Hyphenator::makeUpperCase(const OUString& aTerm, CharClass * pCC)
768 {
769     if (pCC)
770         return pCC->toUpper_rtl(aTerm, 0, aTerm.getLength());
771     return aTerm;
772 }
773 
774 
775 OUString SAL_CALL Hyphenator::makeInitCap(const OUString& aTerm, CharClass * pCC)
776 {
777     sal_Int32 tlen = aTerm.getLength();
778     if ((pCC) && (tlen))
779     {
780         OUString bTemp = aTerm.copy(0,1);
781         if (tlen > 1)
782             return ( pCC->toUpper_rtl(bTemp, 0, 1) + pCC->toLower_rtl(aTerm,1,(tlen-1)) );
783 
784         return pCC->toUpper_rtl(bTemp, 0, 1);
785 	}
786     return aTerm;
787 }
788 
789 
790 Reference< XInterface > SAL_CALL Hyphenator_CreateInstance(
791         const Reference< XMultiServiceFactory > & /*rSMgr*/ )
792         throw(Exception)
793 {
794 	Reference< XInterface > xService = (cppu::OWeakObject*) new Hyphenator;
795 	return xService;
796 }
797 
798 
799 sal_Bool SAL_CALL Hyphenator::addLinguServiceEventListener(
800         const Reference< XLinguServiceEventListener >& rxLstnr )
801         throw(RuntimeException)
802 {
803 	MutexGuard	aGuard( GetLinguMutex() );
804 
805 	sal_Bool bRes = sal_False;
806 	if (!bDisposing && rxLstnr.is())
807 	{
808 		bRes = GetPropHelper().addLinguServiceEventListener( rxLstnr );
809 	}
810 	return bRes;
811 }
812 
813 
814 sal_Bool SAL_CALL Hyphenator::removeLinguServiceEventListener(
815         const Reference< XLinguServiceEventListener >& rxLstnr )
816         throw(RuntimeException)
817 {
818 	MutexGuard	aGuard( GetLinguMutex() );
819 
820 	sal_Bool bRes = sal_False;
821 	if (!bDisposing && rxLstnr.is())
822 	{
823 		DBG_ASSERT( xPropHelper.is(), "xPropHelper non existent" );
824 		bRes = GetPropHelper().removeLinguServiceEventListener( rxLstnr );
825 	}
826 	return bRes;
827 }
828 
829 
830 OUString SAL_CALL Hyphenator::getServiceDisplayName( const Locale& /*rLocale*/ )
831         throw(RuntimeException)
832 {
833 	MutexGuard	aGuard( GetLinguMutex() );
834 	return A2OU( "Libhyphen Hyphenator" );
835 }
836 
837 
838 void SAL_CALL Hyphenator::initialize( const Sequence< Any >& rArguments )
839 		throw(Exception, RuntimeException)
840 {
841 	MutexGuard	aGuard( GetLinguMutex() );
842 
843 	if (!pPropHelper)
844 	{
845 		sal_Int32 nLen = rArguments.getLength();
846 		if (2 == nLen)
847 		{
848 			Reference< XPropertySet	>	xPropSet;
849 			rArguments.getConstArray()[0] >>= xPropSet;
850 			//rArguments.getConstArray()[1] >>= xDicList;
851 
852 			//! Pointer allows for access of the non-UNO functions.
853 			//! And the reference to the UNO-functions while increasing
854 			//! the ref-count and will implicitly free the memory
855 			//! when the object is not longer used.
856 			pPropHelper = new PropertyHelper_Hyphen( (XHyphenator *) this, xPropSet );
857 			xPropHelper = pPropHelper;
858 			pPropHelper->AddAsPropListener();	//! after a reference is established
859 		}
860         else
861         {
862 			DBG_ERROR( "wrong number of arguments in sequence" );
863         }
864 	}
865 }
866 
867 
868 void SAL_CALL Hyphenator::dispose()
869 		throw(RuntimeException)
870 {
871 	MutexGuard	aGuard( GetLinguMutex() );
872 
873 	if (!bDisposing)
874 	{
875 		bDisposing = sal_True;
876 		EventObject	aEvtObj( (XHyphenator *) this );
877 		aEvtListeners.disposeAndClear( aEvtObj );
878 	}
879 }
880 
881 
882 void SAL_CALL Hyphenator::addEventListener( const Reference< XEventListener >& rxListener )
883 		throw(RuntimeException)
884 {
885 	MutexGuard	aGuard( GetLinguMutex() );
886 
887 	if (!bDisposing && rxListener.is())
888 		aEvtListeners.addInterface( rxListener );
889 }
890 
891 
892 void SAL_CALL Hyphenator::removeEventListener( const Reference< XEventListener >& rxListener )
893 		throw(RuntimeException)
894 {
895 	MutexGuard	aGuard( GetLinguMutex() );
896 
897 	if (!bDisposing && rxListener.is())
898 		aEvtListeners.removeInterface( rxListener );
899 }
900 
901 
902 ///////////////////////////////////////////////////////////////////////////
903 // Service specific part
904 //
905 
906 OUString SAL_CALL Hyphenator::getImplementationName()
907 		throw(RuntimeException)
908 {
909 	MutexGuard	aGuard( GetLinguMutex() );
910 
911 	return getImplementationName_Static();
912 }
913 
914 
915 sal_Bool SAL_CALL Hyphenator::supportsService( const OUString& ServiceName )
916 		throw(RuntimeException)
917 {
918 	MutexGuard	aGuard( GetLinguMutex() );
919 
920 	Sequence< OUString > aSNL = getSupportedServiceNames();
921 	const OUString * pArray = aSNL.getConstArray();
922 	for( sal_Int32 i = 0; i < aSNL.getLength(); i++ )
923 		if( pArray[i] == ServiceName )
924 			return sal_True;
925 	return sal_False;
926 }
927 
928 
929 Sequence< OUString > SAL_CALL Hyphenator::getSupportedServiceNames()
930 		throw(RuntimeException)
931 {
932 	MutexGuard	aGuard( GetLinguMutex() );
933 
934 	return getSupportedServiceNames_Static();
935 }
936 
937 
938 Sequence< OUString > Hyphenator::getSupportedServiceNames_Static()
939 		throw()
940 {
941 	MutexGuard	aGuard( GetLinguMutex() );
942 
943 	Sequence< OUString > aSNS( 1 );	// auch mehr als 1 Service moeglich
944 	aSNS.getArray()[0] = A2OU( SN_HYPHENATOR );
945 	return aSNS;
946 }
947 
948 void * SAL_CALL Hyphenator_getFactory( const sal_Char * pImplName,
949 			XMultiServiceFactory * pServiceManager, void *  )
950 {
951 	void * pRet = 0;
952 	if ( !Hyphenator::getImplementationName_Static().compareToAscii( pImplName ) )
953 	{
954 		Reference< XSingleServiceFactory > xFactory =
955 			cppu::createOneInstanceFactory(
956 				pServiceManager,
957 				Hyphenator::getImplementationName_Static(),
958 				Hyphenator_CreateInstance,
959 				Hyphenator::getSupportedServiceNames_Static());
960 		// acquire, because we return an interface pointer instead of a reference
961 		xFactory->acquire();
962 		pRet = xFactory.get();
963 	}
964 	return pRet;
965 }
966 
967 
968 ///////////////////////////////////////////////////////////////////////////
969 
970 #undef CAPTYPE_UNKNOWN
971 #undef CAPTYPE_NOCAP
972 #undef CAPTYPE_INITCAP
973 #undef CAPTYPE_ALLCAP
974 #undef CAPTYPE_MIXED
975