2 /*************************************************************************
4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
6 * Copyright 2008 by Sun Microsystems, Inc.
8 * OpenOffice.org - a multi-platform office productivity suite
10 * $RCSfile: hyphdsp.cxx,v $
13 * This file is part of OpenOffice.org.
15 * OpenOffice.org is free software: you can redistribute it and/or modify
16 * it under the terms of the GNU Lesser General Public License version 3
17 * only, as published by the Free Software Foundation.
19 * OpenOffice.org is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU Lesser General Public License version 3 for more details
23 * (a copy is included in the LICENSE file that accompanied this code).
25 * You should have received a copy of the GNU Lesser General Public License
26 * version 3 along with OpenOffice.org. If not, see
27 * <http://www.openoffice.org/license.html>
28 * for a copy of the LGPLv3 License.
30 ************************************************************************/
32 // MARKER(update_precomp.py): autogen include statement, do not remove
33 #include "precompiled_linguistic.hxx"
36 #include <cppuhelper/factory.hxx> // helper for factories
37 #include <com/sun/star/registry/XRegistryKey.hpp>
38 #include <com/sun/star/linguistic2/XSearchableDictionaryList.hpp>
39 #include <com/sun/star/linguistic2/XHyphenatedWord.hpp>
40 #include <rtl/ustrbuf.hxx>
41 #include <i18npool/lang.h>
42 #include <unotools/localedatawrapper.hxx>
43 #include <tools/debug.hxx>
44 #include <svtools/lngmisc.hxx>
45 #include <unotools/processfactory.hxx>
46 #include <osl/mutex.hxx>
48 #include "hyphdsp.hxx"
49 #include "hyphdta.hxx"
50 #include "lngprops.hxx"
51 #include "lngsvcmgr.hxx"
57 using namespace com::sun::star
;
58 using namespace com::sun::star::beans
;
59 using namespace com::sun::star::lang
;
60 using namespace com::sun::star::uno
;
61 using namespace com::sun::star::linguistic2
;
62 using namespace linguistic
;
64 ///////////////////////////////////////////////////////////////////////////
66 HyphenatorDispatcher::HyphenatorDispatcher( LngSvcMgr
&rLngSvcMgr
) :
72 HyphenatorDispatcher::~HyphenatorDispatcher()
78 void HyphenatorDispatcher::ClearSvcList()
80 // release memory for each table entry
81 HyphSvcByLangMap_t aTmp
;
86 Reference
<XHyphenatedWord
> HyphenatorDispatcher::buildHyphWord(
87 const OUString rOrigWord
,
88 const Reference
<XDictionaryEntry
> &xEntry
,
89 INT16 nLang
, INT16 nMaxLeading
)
91 MutexGuard
aGuard( GetLinguMutex() );
93 Reference
< XHyphenatedWord
> xRes
;
97 OUString
aText( xEntry
->getDictionaryWord() );
98 INT32 nTextLen
= aText
.getLength();
100 // trailing '=' means "hyphenation should not be possible"
101 if (nTextLen
> 0 && aText
[ nTextLen
- 1 ] != '=')
103 INT16 nHyphenationPos
= -1;
105 OUStringBuffer
aTmp( nTextLen
);
109 for (INT32 i
= 0; i
< nTextLen
; i
++)
111 sal_Unicode cTmp
= aText
[i
];
121 if (!bSkip
&& nHyphIdx
>= 0)
123 if (nLeading
<= nMaxLeading
)
124 nHyphenationPos
= (INT16
) nHyphIdx
;
126 bSkip
= TRUE
; //! multiple '=' should count as one only
130 if (nHyphenationPos
> 0)
132 aText
= aTmp
.makeStringAndClear();
134 #if OSL_DEBUG_LEVEL > 1
136 if (aText
!= rOrigWord
)
138 // both words should only differ by a having a trailing '.'
139 // character or not...
140 OUString aShorter
, aLonger
;
141 if (aText
.getLength() <= rOrigWord
.getLength())
148 aShorter
= rOrigWord
;
151 xub_StrLen nS
= sal::static_int_cast
< xub_StrLen
>( aShorter
.getLength() );
152 xub_StrLen nL
= sal::static_int_cast
< xub_StrLen
>( aLonger
.getLength() );
153 if (nS
> 0 && nL
> 0)
155 DBG_ASSERT( (nS
+ 1 == nL
) && aLonger
[nL
-1] == (sal_Unicode
) '.',
156 "HyphenatorDispatcher::buildHyphWord: unexpected difference between words!" );
161 //! take care of #i22591#
164 DBG_ASSERT( aText
== rOrigWord
, "failed to " );
165 xRes
= new HyphenatedWord( aText
, nLang
, nHyphenationPos
,
166 aText
, nHyphenationPos
);
175 Reference
< XPossibleHyphens
> HyphenatorDispatcher::buildPossHyphens(
176 const Reference
< XDictionaryEntry
> &xEntry
, INT16 nLanguage
)
178 MutexGuard
aGuard( GetLinguMutex() );
180 Reference
<XPossibleHyphens
> xRes
;
184 // text with hyphenation info
185 OUString
aText( xEntry
->getDictionaryWord() );
186 INT32 nTextLen
= aText
.getLength();
188 // trailing '=' means "hyphenation should not be possible"
189 if (nTextLen
> 0 && aText
[ nTextLen
- 1 ] != '=')
191 // sequence to hold hyphenation positions
192 Sequence
< INT16
> aHyphPos( nTextLen
);
193 INT16
*pPos
= aHyphPos
.getArray();
194 INT32 nHyphCount
= 0;
196 OUStringBuffer
aTmp( nTextLen
);
199 for (INT32 i
= 0; i
< nTextLen
; i
++)
201 sal_Unicode cTmp
= aText
[i
];
210 if (!bSkip
&& nHyphIdx
>= 0)
211 pPos
[ nHyphCount
++ ] = (INT16
) nHyphIdx
;
212 bSkip
= TRUE
; //! multiple '=' should count as one only
216 // ignore (multiple) trailing '='
217 if (bSkip
&& nHyphIdx
>= 0)
221 DBG_ASSERT( nHyphCount
>= 0, "lng : invalid hyphenation count");
225 aHyphPos
.realloc( nHyphCount
);
226 xRes
= new PossibleHyphens( aTmp
.makeStringAndClear(), nLanguage
,
236 Sequence
< Locale
> SAL_CALL
HyphenatorDispatcher::getLocales()
237 throw(RuntimeException
)
239 MutexGuard
aGuard( GetLinguMutex() );
241 Sequence
< Locale
> aLocales( static_cast< sal_Int32
>(aSvcMap
.size()) );
242 Locale
*pLocales
= aLocales
.getArray();
243 HyphSvcByLangMap_t::const_iterator aIt
;
244 for (aIt
= aSvcMap
.begin(); aIt
!= aSvcMap
.end(); ++aIt
)
246 *pLocales
++ = CreateLocale( aIt
->first
);
252 BOOL SAL_CALL
HyphenatorDispatcher::hasLocale(const Locale
& rLocale
)
253 throw(RuntimeException
)
255 MutexGuard
aGuard( GetLinguMutex() );
256 HyphSvcByLangMap_t::const_iterator
aIt( aSvcMap
.find( LocaleToLanguage( rLocale
) ) );
257 return aIt
!= aSvcMap
.end();
261 Reference
< XHyphenatedWord
> SAL_CALL
262 HyphenatorDispatcher::hyphenate(
263 const OUString
& rWord
, const Locale
& rLocale
, sal_Int16 nMaxLeading
,
264 const PropertyValues
& rProperties
)
265 throw(IllegalArgumentException
, RuntimeException
)
267 MutexGuard
aGuard( GetLinguMutex() );
269 Reference
< XHyphenatedWord
> xRes
;
271 INT32 nWordLen
= rWord
.getLength();
272 INT16 nLanguage
= LocaleToLanguage( rLocale
);
273 if (nLanguage
== LANGUAGE_NONE
|| !nWordLen
||
274 nMaxLeading
== 0 || nMaxLeading
== nWordLen
)
277 // search for entry with that language
278 LangSvcEntries_Hyph
*pEntry
= aSvcMap
[ nLanguage
].get();
280 BOOL bWordModified
= FALSE
;
281 if (!pEntry
|| (nMaxLeading
< 0 || nMaxLeading
> nWordLen
))
283 #ifdef LINGU_EXCEPTIONS
284 throw IllegalArgumentException();
291 OUString
aChkWord( rWord
);
293 // replace typographical apostroph by ascii apostroph
294 String
aSingleQuote( GetLocaleDataWrapper( nLanguage
).getQuotationMarkEnd() );
295 DBG_ASSERT( 1 == aSingleQuote
.Len(), "unexpectend length of quotation mark" );
296 if (aSingleQuote
.Len())
297 aChkWord
= aChkWord
.replace( aSingleQuote
.GetChar(0), '\'' );
299 bWordModified
|= RemoveHyphens( aChkWord
);
300 if (IsIgnoreControlChars( rProperties
, GetPropSet() ))
301 bWordModified
|= RemoveControlChars( aChkWord
);
302 INT16 nChkMaxLeading
= (INT16
) GetPosInWordToCheck( rWord
, nMaxLeading
);
304 // check for results from (positive) dictionaries which have precedence!
305 Reference
< XDictionaryEntry
> xEntry
;
307 if (GetDicList().is() && IsUseDicList( rProperties
, GetPropSet() ))
309 xEntry
= GetDicList()->queryDictionaryEntry( aChkWord
, rLocale
,
315 //! because queryDictionaryEntry (in the end DictionaryNeo::getEntry)
316 //! does not distinguish betwee "XYZ" and "XYZ." in order to avoid
317 //! to require them as different entry we have to supply the
318 //! original word here as well so it can be used in th result
319 //! otherwise a strange effect may occur (see #i22591#)
320 xRes
= buildHyphWord( rWord
, xEntry
, nLanguage
, nChkMaxLeading
);
324 INT32 nLen
= pEntry
->aSvcImplNames
.getLength() > 0 ? 1 : 0;
325 DBG_ASSERT( pEntry
->nLastTriedSvcIndex
< nLen
,
326 "lng : index out of range");
329 Reference
< XHyphenator
> xHyph
;
330 if (pEntry
->aSvcRefs
.getLength() > 0)
331 xHyph
= pEntry
->aSvcRefs
[0];
333 // try already instantiated service
334 if (i
<= pEntry
->nLastTriedSvcIndex
)
336 if (xHyph
.is() && xHyph
->hasLocale( rLocale
))
337 xRes
= xHyph
->hyphenate( aChkWord
, rLocale
, nChkMaxLeading
,
341 else if (pEntry
->nLastTriedSvcIndex
< nLen
- 1)
342 // instantiate services and try it
344 // const OUString *pImplNames = pEntry->aSvcImplNames.getConstArray();
345 Reference
< XHyphenator
> *pRef
= pEntry
->aSvcRefs
.getArray();
347 Reference
< XMultiServiceFactory
> xMgr( getProcessServiceFactory() );
350 // build service initialization argument
351 Sequence
< Any
> aArgs(2);
352 aArgs
.getArray()[0] <<= GetPropSet();
353 //! The dispatcher searches the dictionary-list
354 //! thus the service needs not to now about it
355 //aArgs.getArray()[1] <<= GetDicList();
357 // create specific service via it's implementation name
360 xHyph
= Reference
< XHyphenator
>(
361 xMgr
->createInstanceWithArguments(
362 pEntry
->aSvcImplNames
[0], aArgs
), UNO_QUERY
);
364 catch (uno::Exception
&)
366 DBG_ASSERT( 0, "createInstanceWithArguments failed" );
370 Reference
< XLinguServiceEventBroadcaster
>
371 xBroadcaster( xHyph
, UNO_QUERY
);
372 if (xBroadcaster
.is())
373 rMgr
.AddLngSvcEvtBroadcaster( xBroadcaster
);
375 if (xHyph
.is() && xHyph
->hasLocale( rLocale
))
376 xRes
= xHyph
->hyphenate( aChkWord
, rLocale
, nChkMaxLeading
,
379 pEntry
->nLastTriedSvcIndex
= (INT16
) i
;
382 // if language is not supported by the services
383 // remove it from the list.
384 if (xHyph
.is() && !xHyph
->hasLocale( rLocale
))
385 aSvcMap
.erase( nLanguage
);
388 } // if (xEntry.is())
391 if (bWordModified
&& xRes
.is())
392 xRes
= RebuildHyphensAndControlChars( rWord
, xRes
);
394 if (xRes
.is() && xRes
->getWord() != rWord
)
396 xRes
= new HyphenatedWord( rWord
, nLanguage
, xRes
->getHyphenationPos(),
397 xRes
->getHyphenatedWord(),
398 xRes
->getHyphenPos() );
405 Reference
< XHyphenatedWord
> SAL_CALL
406 HyphenatorDispatcher::queryAlternativeSpelling(
407 const OUString
& rWord
, const Locale
& rLocale
, sal_Int16 nIndex
,
408 const PropertyValues
& rProperties
)
409 throw(IllegalArgumentException
, RuntimeException
)
411 MutexGuard
aGuard( GetLinguMutex() );
413 Reference
< XHyphenatedWord
> xRes
;
415 INT32 nWordLen
= rWord
.getLength();
416 INT16 nLanguage
= LocaleToLanguage( rLocale
);
417 if (nLanguage
== LANGUAGE_NONE
|| !nWordLen
)
420 // search for entry with that language
421 LangSvcEntries_Hyph
*pEntry
= aSvcMap
[ nLanguage
].get();
423 BOOL bWordModified
= FALSE
;
424 if (!pEntry
|| !(0 <= nIndex
&& nIndex
<= nWordLen
- 2))
426 #ifdef LINGU_EXCEPTIONS
427 throw IllegalArgumentException();
434 OUString
aChkWord( rWord
);
436 // replace typographical apostroph by ascii apostroph
437 String
aSingleQuote( GetLocaleDataWrapper( nLanguage
).getQuotationMarkEnd() );
438 DBG_ASSERT( 1 == aSingleQuote
.Len(), "unexpectend length of quotation mark" );
439 if (aSingleQuote
.Len())
440 aChkWord
= aChkWord
.replace( aSingleQuote
.GetChar(0), '\'' );
442 bWordModified
|= RemoveHyphens( aChkWord
);
443 if (IsIgnoreControlChars( rProperties
, GetPropSet() ))
444 bWordModified
|= RemoveControlChars( aChkWord
);
445 INT16 nChkIndex
= (INT16
) GetPosInWordToCheck( rWord
, nIndex
);
447 // check for results from (positive) dictionaries which have precedence!
448 Reference
< XDictionaryEntry
> xEntry
;
450 if (GetDicList().is() && IsUseDicList( rProperties
, GetPropSet() ))
452 xEntry
= GetDicList()->queryDictionaryEntry( aChkWord
, rLocale
,
458 //! alternative spellings not yet supported by dictionaries
462 INT32 nLen
= pEntry
->aSvcImplNames
.getLength() > 0 ? 1 : 0;
463 DBG_ASSERT( pEntry
->nLastTriedSvcIndex
< nLen
,
464 "lng : index out of range");
467 Reference
< XHyphenator
> xHyph
;
468 if (pEntry
->aSvcRefs
.getLength() > 0)
469 xHyph
= pEntry
->aSvcRefs
[0];
471 // try already instantiated service
472 if (i
<= pEntry
->nLastTriedSvcIndex
)
474 if (xHyph
.is() && xHyph
->hasLocale( rLocale
))
475 xRes
= xHyph
->queryAlternativeSpelling( aChkWord
, rLocale
,
476 nChkIndex
, rProperties
);
479 else if (pEntry
->nLastTriedSvcIndex
< nLen
- 1)
480 // instantiate services and try it
482 // const OUString *pImplNames = pEntry->aSvcImplNames.getConstArray();
483 Reference
< XHyphenator
> *pRef
= pEntry
->aSvcRefs
.getArray();
485 Reference
< XMultiServiceFactory
> xMgr( getProcessServiceFactory() );
488 // build service initialization argument
489 Sequence
< Any
> aArgs(2);
490 aArgs
.getArray()[0] <<= GetPropSet();
491 //! The dispatcher searches the dictionary-list
492 //! thus the service needs not to now about it
493 //aArgs.getArray()[1] <<= GetDicList();
495 // create specific service via it's implementation name
498 xHyph
= Reference
< XHyphenator
>(
499 xMgr
->createInstanceWithArguments(
500 pEntry
->aSvcImplNames
[0], aArgs
), UNO_QUERY
);
502 catch (uno::Exception
&)
504 DBG_ASSERT( 0, "createInstanceWithArguments failed" );
508 Reference
< XLinguServiceEventBroadcaster
>
509 xBroadcaster( xHyph
, UNO_QUERY
);
510 if (xBroadcaster
.is())
511 rMgr
.AddLngSvcEvtBroadcaster( xBroadcaster
);
513 if (xHyph
.is() && xHyph
->hasLocale( rLocale
))
514 xRes
= xHyph
->queryAlternativeSpelling( aChkWord
, rLocale
,
515 nChkIndex
, rProperties
);
517 pEntry
->nLastTriedSvcIndex
= (INT16
) i
;
520 // if language is not supported by the services
521 // remove it from the list.
522 if (xHyph
.is() && !xHyph
->hasLocale( rLocale
))
523 aSvcMap
.erase( nLanguage
);
526 } // if (xEntry.is())
529 if (bWordModified
&& xRes
.is())
530 xRes
= RebuildHyphensAndControlChars( rWord
, xRes
);
532 if (xRes
.is() && xRes
->getWord() != rWord
)
534 xRes
= new HyphenatedWord( rWord
, nLanguage
, xRes
->getHyphenationPos(),
535 xRes
->getHyphenatedWord(),
536 xRes
->getHyphenPos() );
543 Reference
< XPossibleHyphens
> SAL_CALL
544 HyphenatorDispatcher::createPossibleHyphens(
545 const OUString
& rWord
, const Locale
& rLocale
,
546 const PropertyValues
& rProperties
)
547 throw(IllegalArgumentException
, RuntimeException
)
549 MutexGuard
aGuard( GetLinguMutex() );
551 Reference
< XPossibleHyphens
> xRes
;
553 INT16 nLanguage
= LocaleToLanguage( rLocale
);
554 if (nLanguage
== LANGUAGE_NONE
|| !rWord
.getLength())
557 // search for entry with that language
558 LangSvcEntries_Hyph
*pEntry
= aSvcMap
[ nLanguage
].get();
562 #ifdef LINGU_EXCEPTIONS
563 throw IllegalArgumentException();
568 OUString
aChkWord( rWord
);
570 // replace typographical apostroph by ascii apostroph
571 String
aSingleQuote( GetLocaleDataWrapper( nLanguage
).getQuotationMarkEnd() );
572 DBG_ASSERT( 1 == aSingleQuote
.Len(), "unexpectend length of quotation mark" );
573 if (aSingleQuote
.Len())
574 aChkWord
= aChkWord
.replace( aSingleQuote
.GetChar(0), '\'' );
576 RemoveHyphens( aChkWord
);
577 if (IsIgnoreControlChars( rProperties
, GetPropSet() ))
578 RemoveControlChars( aChkWord
);
580 // check for results from (positive) dictionaries which have precedence!
581 Reference
< XDictionaryEntry
> xEntry
;
583 if (GetDicList().is() && IsUseDicList( rProperties
, GetPropSet() ))
585 xEntry
= GetDicList()->queryDictionaryEntry( aChkWord
, rLocale
,
591 xRes
= buildPossHyphens( xEntry
, nLanguage
);
595 INT32 nLen
= pEntry
->aSvcImplNames
.getLength() > 0 ? 1 : 0;
596 DBG_ASSERT( pEntry
->nLastTriedSvcIndex
< nLen
,
597 "lng : index out of range");
600 Reference
< XHyphenator
> xHyph
;
601 if (pEntry
->aSvcRefs
.getLength() > 0)
602 xHyph
= pEntry
->aSvcRefs
[0];
604 // try already instantiated service
605 if (i
<= pEntry
->nLastTriedSvcIndex
)
607 if (xHyph
.is() && xHyph
->hasLocale( rLocale
))
608 xRes
= xHyph
->createPossibleHyphens( aChkWord
, rLocale
,
612 else if (pEntry
->nLastTriedSvcIndex
< nLen
- 1)
613 // instantiate services and try it
615 // const OUString *pImplNames = pEntry->aSvcImplNames.getConstArray();
616 Reference
< XHyphenator
> *pRef
= pEntry
->aSvcRefs
.getArray();
618 Reference
< XMultiServiceFactory
> xMgr( getProcessServiceFactory() );
621 // build service initialization argument
622 Sequence
< Any
> aArgs(2);
623 aArgs
.getArray()[0] <<= GetPropSet();
624 //! The dispatcher searches the dictionary-list
625 //! thus the service needs not to now about it
626 //aArgs.getArray()[1] <<= GetDicList();
628 // create specific service via it's implementation name
631 xHyph
= Reference
< XHyphenator
>(
632 xMgr
->createInstanceWithArguments(
633 pEntry
->aSvcImplNames
[0], aArgs
), UNO_QUERY
);
635 catch (uno::Exception
&)
637 DBG_ASSERT( 0, "createWithArguments failed" );
641 Reference
< XLinguServiceEventBroadcaster
>
642 xBroadcaster( xHyph
, UNO_QUERY
);
643 if (xBroadcaster
.is())
644 rMgr
.AddLngSvcEvtBroadcaster( xBroadcaster
);
646 if (xHyph
.is() && xHyph
->hasLocale( rLocale
))
647 xRes
= xHyph
->createPossibleHyphens( aChkWord
, rLocale
,
650 pEntry
->nLastTriedSvcIndex
= (INT16
) i
;
653 // if language is not supported by the services
654 // remove it from the list.
655 if (xHyph
.is() && !xHyph
->hasLocale( rLocale
))
656 aSvcMap
.erase( nLanguage
);
659 } // if (xEntry.is())
662 if (xRes
.is() && xRes
->getWord() != rWord
)
664 xRes
= new PossibleHyphens( rWord
, nLanguage
,
665 xRes
->getPossibleHyphens(),
666 xRes
->getHyphenationPositions() );
673 void HyphenatorDispatcher::SetServiceList( const Locale
&rLocale
,
674 const Sequence
< OUString
> &rSvcImplNames
)
676 MutexGuard
aGuard( GetLinguMutex() );
678 INT16 nLanguage
= LocaleToLanguage( rLocale
);
680 INT32 nLen
= rSvcImplNames
.getLength();
683 aSvcMap
.erase( nLanguage
);
687 LangSvcEntries_Hyph
*pEntry
= aSvcMap
[ nLanguage
].get();
688 // only one hypenator can be in use for a language...
689 //const OUString &rSvcImplName = rSvcImplNames.getConstArray()[0];
693 pEntry
->aSvcImplNames
= rSvcImplNames
;
694 pEntry
->aSvcImplNames
.realloc(1);
695 pEntry
->aSvcRefs
= Sequence
< Reference
< XHyphenator
> > ( 1 );
699 boost::shared_ptr
< LangSvcEntries_Hyph
> pTmpEntry( new LangSvcEntries_Hyph( rSvcImplNames
[0] ) );
700 pTmpEntry
->aSvcRefs
= Sequence
< Reference
< XHyphenator
> >( 1 );
701 aSvcMap
[ nLanguage
] = pTmpEntry
;
708 HyphenatorDispatcher::GetServiceList( const Locale
&rLocale
) const
710 MutexGuard
aGuard( GetLinguMutex() );
712 Sequence
< OUString
> aRes
;
714 // search for entry with that language and use data from that
715 INT16 nLanguage
= LocaleToLanguage( rLocale
);
716 HyphenatorDispatcher
*pThis
= (HyphenatorDispatcher
*) this;
717 const LangSvcEntries_Hyph
*pEntry
= pThis
->aSvcMap
[ nLanguage
].get();
720 aRes
= pEntry
->aSvcImplNames
;
721 if (aRes
.getLength() > 0)
729 LinguDispatcher::DspType
HyphenatorDispatcher::GetDspType() const
735 ///////////////////////////////////////////////////////////////////////////