1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: dicimp.cxx,v $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_linguistic.hxx"
33 #include <i18npool/lang.h>
39 #include <hyphdsp.hxx>
41 #include <tools/urlobj.hxx>
42 #include <tools/debug.hxx>
43 #include <tools/fsys.hxx>
44 #include <tools/stream.hxx>
45 #include <tools/string.hxx>
46 #include <sfx2/docfile.hxx>
47 #include <osl/mutex.hxx>
48 #include <unotools/processfactory.hxx>
49 #include <i18npool/mslangid.hxx>
51 #include <com/sun/star/linguistic2/DictionaryType.hpp>
52 #include <com/sun/star/linguistic2/DictionaryEventFlags.hpp>
53 #include <com/sun/star/registry/XRegistryKey.hpp>
55 #include <cppuhelper/factory.hxx> // helper for factories
60 using namespace com::sun::star
;
61 using namespace com::sun::star::lang
;
62 using namespace com::sun::star::uno
;
63 using namespace com::sun::star::linguistic2
;
64 using namespace linguistic
;
66 ///////////////////////////////////////////////////////////////////////////
69 #define VERS2_NOLANGUAGE 1024
71 #define MAX_HEADER_LENGTH 16
73 static const sal_Char
* pDicExt
= "dic";
74 static const sal_Char
* pVerStr2
= "WBSWG2";
75 static const sal_Char
* pVerStr5
= "WBSWG5";
76 static const sal_Char
* pVerStr6
= "WBSWG6";
77 static const sal_Char
* pVerOOo7
= "OOoUserDict1";
79 static sal_Bool
getTag(const ByteString
&rLine
,
80 const sal_Char
*pTagName
, ByteString
&rTagValue
)
82 xub_StrLen nPos
= rLine
.Search( pTagName
);
83 if (nPos
== STRING_NOTFOUND
)
86 rTagValue
= rLine
.Copy( nPos
+ sal::static_int_cast
< xub_StrLen
>(strlen( pTagName
)) ).EraseLeadingAndTrailingChars();
91 INT16
ReadDicVersion( SvStream
*pStream
, USHORT
&nLng
, BOOL
&bNeg
)
95 sal_Char pMagicHeader
[MAX_HEADER_LENGTH
];
100 if (!pStream
|| pStream
->GetError())
103 sal_Size nSniffPos
= pStream
->Tell();
104 static sal_Size nVerOOo7Len
= sal::static_int_cast
< sal_Size
>(strlen( pVerOOo7
));
105 pMagicHeader
[ nVerOOo7Len
] = '\0';
106 if ((pStream
->Read((void *) pMagicHeader
, nVerOOo7Len
) == nVerOOo7Len
) &&
107 !strcmp(pMagicHeader
, pVerOOo7
))
114 // 1st skip magic / header line
115 pStream
->ReadLine(aLine
);
117 // 2nd line: language all | en-US | pt-BR ...
118 while (sal_True
== (bSuccess
= pStream
->ReadLine(aLine
)))
120 ByteString aTagValue
;
122 if (aLine
.GetChar(0) == '#') // skip comments
126 if (getTag(aLine
, "lang: ", aTagValue
))
128 if (aTagValue
== "<none>")
129 nLng
= LANGUAGE_NONE
;
131 nLng
= MsLangId::convertIsoStringToLanguage(OUString(aTagValue
.GetBuffer(),
132 aTagValue
.Len(), RTL_TEXTENCODING_ASCII_US
));
135 // type: negative / positive
136 if (getTag(aLine
, "type: ", aTagValue
))
138 if (aTagValue
== "negative")
144 if (aLine
.Search ("---") != STRING_NOTFOUND
) // end of header
154 pStream
->Seek (nSniffPos
);
157 if (nLen
>= MAX_HEADER_LENGTH
)
160 pStream
->Read(pMagicHeader
, nLen
);
161 pMagicHeader
[nLen
] = '\0';
163 // Check version magic
164 if (0 == strcmp( pMagicHeader
, pVerStr6
))
166 else if (0 == strcmp( pMagicHeader
, pVerStr5
))
168 else if (0 == strcmp( pMagicHeader
, pVerStr2
))
173 if (2 == nDicVersion
||
177 // The language of the dictionary
180 if (VERS2_NOLANGUAGE
== nLng
)
181 nLng
= LANGUAGE_NONE
;
195 const String
GetDicExtension()
197 return String::CreateFromAscii( pDicExt
);
200 ///////////////////////////////////////////////////////////////////////////
202 DictionaryNeo::DictionaryNeo() :
203 aDicEvtListeners( GetLinguMutex() ),
204 eDicType (DictionaryType_POSITIVE
),
205 nLanguage (LANGUAGE_NONE
)
209 bNeedEntries
= FALSE
;
210 bIsModified
= bIsActive
= FALSE
;
214 DictionaryNeo::DictionaryNeo(const OUString
&rName
,
215 INT16 nLang
, DictionaryType eType
,
216 const OUString
&rMainURL
,
218 aDicEvtListeners( GetLinguMutex() ),
227 bIsModified
= bIsActive
= FALSE
;
228 bIsReadonly
= !bWriteable
;
230 if( rMainURL
.getLength() > 0 )
232 BOOL bExists
= FileExists( rMainURL
);
235 // save new dictionaries with in 6.0 Format (uses UTF8)
238 //! create physical representation of an **empty** dictionary
239 //! that could be found by the dictionary-list implementation
240 // (Note: empty dictionaries are not just empty files!)
241 DBG_ASSERT( !bIsReadonly
,
242 "DictionaryNeo: dictionaries should be writeable if they are to be saved" );
244 saveEntries( rMainURL
);
245 bNeedEntries
= FALSE
;
250 // non persistent dictionaries (like IgnoreAllList) should always be writable
252 bNeedEntries
= FALSE
;
256 DictionaryNeo::~DictionaryNeo()
260 ULONG
DictionaryNeo::loadEntries(const OUString
&rMainURL
)
262 MutexGuard
aGuard( GetLinguMutex() );
264 // counter check that it is safe to set bIsModified to FALSE at
265 // the end of the function
266 DBG_ASSERT(!bIsModified
, "lng : dictionary already modified!");
268 // function should only be called once in order to load entries from file
269 bNeedEntries
= FALSE
;
271 if (rMainURL
.getLength() == 0)
274 ULONG nErr
= sal::static_int_cast
< ULONG
>(-1);
277 SfxMedium
aMedium( rMainURL
, STREAM_READ
| STREAM_SHARE_DENYWRITE
, FALSE
);
278 SvStream
*pStream
= aMedium
.GetInStream();
285 nDicVersion
= ReadDicVersion(pStream
, nLang
, bNegativ
);
286 if (0 != (nErr
= pStream
->GetError()))
290 eDicType
= bNegativ
? DictionaryType_NEGATIVE
: DictionaryType_POSITIVE
;
292 rtl_TextEncoding eEnc
= osl_getThreadTextEncoding();
293 if (nDicVersion
>= 6)
294 eEnc
= RTL_TEXTENCODING_UTF8
;
297 if (6 == nDicVersion
||
302 sal_Char aWordBuf
[ BUFSIZE
];
304 // Das erste Wort einlesen
305 if (!pStream
->IsEof())
308 if (0 != (nErr
= pStream
->GetError()))
310 if ( nLen
< BUFSIZE
)
312 pStream
->Read(aWordBuf
, nLen
);
313 if (0 != (nErr
= pStream
->GetError()))
315 *(aWordBuf
+ nLen
) = 0;
319 while(!pStream
->IsEof())
321 // Aus dem File einlesen
322 // Einfuegen ins Woerterbuch ohne Konvertierung
325 ByteString
aDummy( aWordBuf
);
326 String
aText( aDummy
, eEnc
);
327 uno::Reference
< XDictionaryEntry
> xEntry
=
328 new DicEntry( aText
, bNegativ
);
329 addEntry_Impl( xEntry
, TRUE
); //! don't launch events here
333 if (pStream
->IsEof()) // #75082# GPF in online-spelling
335 if (0 != (nErr
= pStream
->GetError()))
337 #ifdef LINGU_EXCEPTIONS
339 throw io::IOException() ;
344 pStream
->Read(aWordBuf
, nLen
);
345 if (0 != (nErr
= pStream
->GetError()))
349 return SVSTREAM_READ_ERROR
;
350 *(aWordBuf
+ nLen
) = 0;
353 else if (7 == nDicVersion
)
358 // remaining lines - stock strings (a [==] b)
359 while (sal_True
== (bSuccess
= pStream
->ReadLine(aLine
)))
361 if (aLine
.GetChar(0) == '#') // skip comments
363 rtl::OUString aText
= rtl::OStringToOUString (aLine
, RTL_TEXTENCODING_UTF8
);
364 uno::Reference
< XDictionaryEntry
> xEntry
=
365 new DicEntry( aText
, eDicType
== DictionaryType_NEGATIVE
);
366 addEntry_Impl( xEntry
, TRUE
); //! don't launch events here
370 DBG_ASSERT(isSorted(), "lng : dictionary is not sorted");
372 // since this routine should be called only initialy (prior to any
373 // modification to be saved) we reset the bIsModified flag here that
374 // was implicitly set by addEntry_Impl
377 return pStream
->GetError();
381 static ByteString
formatForSave(
382 const uno::Reference
< XDictionaryEntry
> &xEntry
, rtl_TextEncoding eEnc
)
384 ByteString
aStr(xEntry
->getDictionaryWord().getStr(), eEnc
);
386 if (xEntry
->isNegative())
389 aStr
+= ByteString(xEntry
->getReplacementText().getStr(), eEnc
);
395 ULONG
DictionaryNeo::saveEntries(const OUString
&rURL
)
397 MutexGuard
aGuard( GetLinguMutex() );
399 if (rURL
.getLength() == 0)
402 ULONG nErr
= sal::static_int_cast
< ULONG
>(-1);
404 DBG_ASSERT(!INetURLObject( rURL
).HasError(), "lng : invalid URL");
405 SfxMedium
aMedium( rURL
, STREAM_WRITE
| STREAM_TRUNC
| STREAM_SHARE_DENYALL
,
407 aMedium
.CreateTempFile(); // use temp file to write to...
408 SvStream
*pStream
= aMedium
.GetOutStream();
412 rtl_TextEncoding eEnc
= osl_getThreadTextEncoding();
413 if (nDicVersion
>= 6)
414 eEnc
= RTL_TEXTENCODING_UTF8
;
416 if (nDicVersion
== 7)
418 pStream
->WriteLine(ByteString (pVerOOo7
));
419 if (0 != (nErr
= pStream
->GetError()))
422 if (nLanguage
== LANGUAGE_NONE
)
423 pStream
->WriteLine(ByteString("lang: <none>"));
426 ByteString
aLine("lang: ");
427 aLine
+= ByteString( String( MsLangId::convertLanguageToIsoString( nLanguage
) ), eEnc
);
428 pStream
->WriteLine( aLine
);
430 if (0 != (nErr
= pStream
->GetError()))
433 if (eDicType
== DictionaryType_POSITIVE
)
434 pStream
->WriteLine(ByteString("type: positive"));
436 pStream
->WriteLine(ByteString("type: negative"));
437 if (0 != (nErr
= pStream
->GetError()))
440 pStream
->WriteLine(ByteString("---"));
441 if (0 != (nErr
= pStream
->GetError()))
444 const uno::Reference
< XDictionaryEntry
> *pEntry
= aEntries
.getConstArray();
445 for (INT32 i
= 0; i
< nCount
; i
++)
447 ByteString aOutStr
= formatForSave(pEntry
[i
], eEnc
);
448 pStream
->WriteLine (aOutStr
);
449 if (0 != (nErr
= pStream
->GetError()))
455 sal_Char aWordBuf
[BUFSIZE
];
458 const sal_Char
*pVerStr
= NULL
;
459 if (6 == nDicVersion
)
462 pVerStr
= eDicType
== DictionaryType_POSITIVE
? pVerStr2
: pVerStr5
;
463 strcpy( aWordBuf
, pVerStr
); // #100211# - checked
464 USHORT nLen
= sal::static_int_cast
< USHORT
>(strlen( aWordBuf
));
466 if (0 != (nErr
= pStream
->GetError()))
468 pStream
->Write(aWordBuf
, nLen
);
469 if (0 != (nErr
= pStream
->GetError()))
472 *pStream
<< nLanguage
;
473 if (0 != (nErr
= pStream
->GetError()))
475 *pStream
<< (sal_Char
) (eDicType
== DictionaryType_NEGATIVE
? TRUE
: FALSE
);
476 if (0 != (nErr
= pStream
->GetError()))
479 const uno::Reference
< XDictionaryEntry
> *pEntry
= aEntries
.getConstArray();
480 for (INT32 i
= 0; i
< nCount
; i
++)
482 ByteString aOutStr
= formatForSave(pEntry
[i
], eEnc
);
484 // the old format would fail (mis-calculation of nLen) and write
485 // uninitialized junk for combined len >= BUFSIZE - we truncate
486 // silently here, but BUFSIZE is large anyway.
487 nLen
= aOutStr
.Len();
492 if (0 != (nErr
= pStream
->GetError()))
494 pStream
->Write(aOutStr
.GetBuffer(), nLen
);
495 if (0 != (nErr
= pStream
->GetError()))
500 //! get return value before Stream is destroyed
501 ULONG nError
= pStream
->GetError();
503 // flush file, close it and release any lock
510 void DictionaryNeo::launchEvent(INT16 nEvent
,
511 uno::Reference
< XDictionaryEntry
> xEntry
)
513 MutexGuard
aGuard( GetLinguMutex() );
515 DictionaryEvent aEvt
;
516 aEvt
.Source
= uno::Reference
< XDictionary
>( this );
517 aEvt
.nEvent
= nEvent
;
518 aEvt
.xDictionaryEntry
= xEntry
;
520 cppu::OInterfaceIteratorHelper
aIt( aDicEvtListeners
);
521 while (aIt
.hasMoreElements())
523 uno::Reference
< XDictionaryEventListener
> xRef( aIt
.next(), UNO_QUERY
);
525 xRef
->processDictionaryEvent( aEvt
);
529 int DictionaryNeo::cmpDicEntry(const OUString
& rWord1
,
530 const OUString
&rWord2
,
533 MutexGuard
aGuard( GetLinguMutex() );
535 // returns 0 if rWord1 is equal to rWord2
536 // " a value < 0 if rWord1 is less than rWord2
537 // " a value > 0 if rWord1 is greater than rWord2
541 OUString
aWord1( rWord1
),
543 INT32 nLen1
= aWord1
.getLength(),
544 nLen2
= aWord2
.getLength();
547 const sal_Unicode cChar
= '.';
548 if (nLen1
&& cChar
== aWord1
[ nLen1
- 1 ])
550 if (nLen2
&& cChar
== aWord2
[ nLen2
- 1 ])
554 const sal_Unicode cIgnChar
= '=';
561 sal_Unicode cChar1
= '\0';
562 sal_Unicode cChar2
= '\0';
565 // skip chars to be ignored
566 while (nIdx1
< nLen1
&& (cChar1
= aWord1
[ nIdx1
]) == cIgnChar
)
571 while (nIdx2
< nLen2
&& (cChar2
= aWord2
[ nIdx2
]) == cIgnChar
)
577 if (nIdx1
< nLen1
&& nIdx2
< nLen2
)
579 nDiff
= cChar1
- cChar2
;
585 } while (nIdx1
< nLen1
&& nIdx2
< nLen2
);
591 { // the string with the smallest count of not ignored chars is the
594 // count remaining IgnChars
595 while (nIdx1
< nLen1
)
597 if (aWord1
[ nIdx1
++ ] == cIgnChar
)
600 while (nIdx2
< nLen2
)
602 if (aWord2
[ nIdx2
++ ] == cIgnChar
)
606 nRes
= ((INT32
) nLen1
- nNumIgnChar1
) - ((INT32
) nLen2
- nNumIgnChar2
);
612 BOOL
DictionaryNeo::seekEntry(const OUString
&rWord
,
613 INT32
*pPos
, BOOL bSimilarOnly
)
615 // look for entry with binary search.
616 // return TRUE if found FALSE else.
617 // if pPos != NULL it will become the position of the found entry, or
618 // if that was not found the position where it has to be inserted
619 // to keep the entries sorted
621 MutexGuard
aGuard( GetLinguMutex() );
623 const uno::Reference
< XDictionaryEntry
> *pEntry
= aEntries
.getConstArray();
624 INT32 nUpperIdx
= getCount(),
630 while( nLowerIdx
<= nUpperIdx
)
632 nMidIdx
= (nLowerIdx
+ nUpperIdx
) / 2;
633 DBG_ASSERT(pEntry
[nMidIdx
].is(), "lng : empty entry encountered");
635 int nCmp
= - cmpDicEntry( pEntry
[nMidIdx
]->getDictionaryWord(),
636 rWord
, bSimilarOnly
);
639 if( pPos
) *pPos
= nMidIdx
;
643 nLowerIdx
= nMidIdx
+ 1;
644 else if( nMidIdx
== 0 )
646 if( pPos
) *pPos
= nLowerIdx
;
650 nUpperIdx
= nMidIdx
- 1;
653 if( pPos
) *pPos
= nLowerIdx
;
657 BOOL
DictionaryNeo::isSorted()
661 const uno::Reference
< XDictionaryEntry
> *pEntry
= aEntries
.getConstArray();
662 INT32 nEntries
= getCount();
664 for (i
= 1; i
< nEntries
; i
++)
666 if (cmpDicEntry( pEntry
[i
-1]->getDictionaryWord(),
667 pEntry
[i
]->getDictionaryWord() ) > 0)
676 BOOL
DictionaryNeo::addEntry_Impl(const uno::Reference
< XDictionaryEntry
> xDicEntry
,
679 MutexGuard
aGuard( GetLinguMutex() );
683 if ( bIsLoadEntries
|| (!bIsReadonly
&& xDicEntry
.is()) )
685 BOOL bIsNegEntry
= xDicEntry
->isNegative();
686 BOOL bAddEntry
= !isFull() &&
687 ( ( eDicType
== DictionaryType_POSITIVE
&& !bIsNegEntry
)
688 || ( eDicType
== DictionaryType_NEGATIVE
&& bIsNegEntry
)
689 || ( eDicType
== DictionaryType_MIXED
) );
691 // look for position to insert entry at
692 // if there is already an entry do not insert the new one
697 bFound
= seekEntry( xDicEntry
->getDictionaryWord(), &nPos
);
704 DBG_ASSERT(!bNeedEntries
, "lng : entries still not loaded");
706 if (nCount
>= aEntries
.getLength())
707 aEntries
.realloc( Max(2 * nCount
, nCount
+ 32) );
708 uno::Reference
< XDictionaryEntry
> *pEntry
= aEntries
.getArray();
710 // shift old entries right
712 for (i
= nCount
- 1; i
>= nPos
; i
--)
713 pEntry
[ i
+1 ] = pEntry
[ i
];
714 // insert new entry at specified position
715 pEntry
[ nPos
] = xDicEntry
;
716 DBG_ASSERT(isSorted(), "lng : dictionary entries unsorted");
724 launchEvent( DictionaryEventFlags::ADD_ENTRY
, xDicEntry
);
732 uno::Reference
< XInterface
> SAL_CALL
DictionaryNeo_CreateInstance(
733 const uno::Reference
< XMultiServiceFactory
> & /*rSMgr*/ )
736 uno::Reference
< XInterface
> xService
=
737 (cppu::OWeakObject
*) new DictionaryNeo
;
741 OUString SAL_CALL
DictionaryNeo::getName( )
742 throw(RuntimeException
)
744 MutexGuard
aGuard( GetLinguMutex() );
748 void SAL_CALL
DictionaryNeo::setName( const OUString
& aName
)
749 throw(RuntimeException
)
751 MutexGuard
aGuard( GetLinguMutex() );
753 if (aDicName
!= aName
)
756 launchEvent(DictionaryEventFlags::CHG_NAME
, NULL
);
760 DictionaryType SAL_CALL
DictionaryNeo::getDictionaryType( )
761 throw(RuntimeException
)
763 MutexGuard
aGuard( GetLinguMutex() );
768 void SAL_CALL
DictionaryNeo::setActive( sal_Bool bActivate
)
769 throw(RuntimeException
)
771 MutexGuard
aGuard( GetLinguMutex() );
773 if (bIsActive
!= bActivate
)
775 bIsActive
= bActivate
!= 0;
776 INT16 nEvent
= bIsActive
?
777 DictionaryEventFlags::ACTIVATE_DIC
: DictionaryEventFlags::DEACTIVATE_DIC
;
779 // remove entries from memory if dictionary is deactivated
780 if (bIsActive
== FALSE
)
782 BOOL bIsEmpty
= nCount
== 0;
784 // save entries first if necessary
785 if (bIsModified
&& hasLocation() && !isReadonly())
789 aEntries
.realloc( 0 );
791 bNeedEntries
= !bIsEmpty
;
793 DBG_ASSERT( !bIsModified
|| !hasLocation() || isReadonly(),
794 "lng : dictionary is still modified" );
797 launchEvent(nEvent
, NULL
);
801 sal_Bool SAL_CALL
DictionaryNeo::isActive( )
802 throw(RuntimeException
)
804 MutexGuard
aGuard( GetLinguMutex() );
808 sal_Int32 SAL_CALL
DictionaryNeo::getCount( )
809 throw(RuntimeException
)
811 MutexGuard
aGuard( GetLinguMutex() );
814 loadEntries( aMainURL
);
818 Locale SAL_CALL
DictionaryNeo::getLocale( )
819 throw(RuntimeException
)
821 MutexGuard
aGuard( GetLinguMutex() );
823 return LanguageToLocale( aRes
, nLanguage
);
826 void SAL_CALL
DictionaryNeo::setLocale( const Locale
& aLocale
)
827 throw(RuntimeException
)
829 MutexGuard
aGuard( GetLinguMutex() );
830 INT16 nLanguageP
= LocaleToLanguage( aLocale
);
831 if (!bIsReadonly
&& nLanguage
!= nLanguageP
)
833 nLanguage
= nLanguageP
;
834 bIsModified
= TRUE
; // new language needs to be saved with dictionary
836 launchEvent( DictionaryEventFlags::CHG_LANGUAGE
, NULL
);
840 uno::Reference
< XDictionaryEntry
> SAL_CALL
DictionaryNeo::getEntry(
841 const OUString
& aWord
)
842 throw(RuntimeException
)
844 MutexGuard
aGuard( GetLinguMutex() );
847 loadEntries( aMainURL
);
850 BOOL bFound
= seekEntry( aWord
, &nPos
, TRUE
);
851 DBG_ASSERT( nCount
<= aEntries
.getLength(), "lng : wrong number of entries");
852 DBG_ASSERT(!bFound
|| nPos
< nCount
, "lng : index out of range");
854 return bFound
? aEntries
.getConstArray()[ nPos
]
855 : uno::Reference
< XDictionaryEntry
>();
858 sal_Bool SAL_CALL
DictionaryNeo::addEntry(
859 const uno::Reference
< XDictionaryEntry
>& xDicEntry
)
860 throw(RuntimeException
)
862 MutexGuard
aGuard( GetLinguMutex() );
869 loadEntries( aMainURL
);
870 bRes
= addEntry_Impl( xDicEntry
);
877 DictionaryNeo::add( const OUString
& rWord
, sal_Bool bIsNegative
,
878 const OUString
& rRplcText
)
879 throw(RuntimeException
)
881 MutexGuard
aGuard( GetLinguMutex() );
887 uno::Reference
< XDictionaryEntry
> xEntry
=
888 new DicEntry( rWord
, bIsNegative
, rRplcText
);
889 bRes
= addEntry_Impl( xEntry
);
895 void lcl_SequenceRemoveElementAt(
896 uno::Sequence
< uno::Reference
< XDictionaryEntry
> >& rEntries
, int nPos
)
898 //TODO: helper for SequenceRemoveElementAt available?
899 if(nPos
>= rEntries
.getLength())
901 uno::Sequence
< uno::Reference
< XDictionaryEntry
> > aTmp(rEntries
.getLength() - 1);
902 uno::Reference
< XDictionaryEntry
> * pOrig
= rEntries
.getArray();
903 uno::Reference
< XDictionaryEntry
> * pTemp
= aTmp
.getArray();
905 for(int i
= 0; i
< aTmp
.getLength(); i
++)
909 pTemp
[i
] = pOrig
[i
+ nOffset
];
915 sal_Bool SAL_CALL
DictionaryNeo::remove( const OUString
& aWord
)
916 throw(RuntimeException
)
918 MutexGuard
aGuard( GetLinguMutex() );
920 BOOL bRemoved
= FALSE
;
925 loadEntries( aMainURL
);
928 BOOL bFound
= seekEntry( aWord
, &nPos
);
929 DBG_ASSERT( nCount
< aEntries
.getLength(),
930 "lng : wrong number of entries");
931 DBG_ASSERT(!bFound
|| nPos
< nCount
, "lng : index out of range");
933 // remove element if found
936 // entry to be removed
937 uno::Reference
< XDictionaryEntry
>
938 xDicEntry( aEntries
.getConstArray()[ nPos
] );
939 DBG_ASSERT(xDicEntry
.is(), "lng : dictionary entry is NULL");
943 //! the following call reduces the length of the sequence by 1 also
944 lcl_SequenceRemoveElementAt( aEntries
, nPos
);
945 bRemoved
= bIsModified
= TRUE
;
947 launchEvent( DictionaryEventFlags::DEL_ENTRY
, xDicEntry
);
954 sal_Bool SAL_CALL
DictionaryNeo::isFull( )
955 throw(RuntimeException
)
957 MutexGuard
aGuard( GetLinguMutex() );
960 loadEntries( aMainURL
);
961 return nCount
>= DIC_MAX_ENTRIES
;
964 uno::Sequence
< uno::Reference
< XDictionaryEntry
> >
965 SAL_CALL
DictionaryNeo::getEntries( )
966 throw(RuntimeException
)
968 MutexGuard
aGuard( GetLinguMutex() );
971 loadEntries( aMainURL
);
972 //! return sequence with length equal to the number of dictionary entries
973 //! (internal used sequence may have additional unused elements.)
974 return uno::Sequence
< uno::Reference
< XDictionaryEntry
> >
975 (aEntries
.getConstArray(), nCount
);
979 void SAL_CALL
DictionaryNeo::clear( )
980 throw(RuntimeException
)
982 MutexGuard
aGuard( GetLinguMutex() );
984 if (!bIsReadonly
&& nCount
)
986 // release all references to old entries and provide space for new ones
987 aEntries
= uno::Sequence
< uno::Reference
< XDictionaryEntry
> > ( 32 );
990 bNeedEntries
= FALSE
;
993 launchEvent( DictionaryEventFlags::ENTRIES_CLEARED
, NULL
);
997 sal_Bool SAL_CALL
DictionaryNeo::addDictionaryEventListener(
998 const uno::Reference
< XDictionaryEventListener
>& xListener
)
999 throw(RuntimeException
)
1001 MutexGuard
aGuard( GetLinguMutex() );
1006 INT32 nLen
= aDicEvtListeners
.getLength();
1007 bRes
= aDicEvtListeners
.addInterface( xListener
) != nLen
;
1012 sal_Bool SAL_CALL
DictionaryNeo::removeDictionaryEventListener(
1013 const uno::Reference
< XDictionaryEventListener
>& xListener
)
1014 throw(RuntimeException
)
1016 MutexGuard
aGuard( GetLinguMutex() );
1021 INT32 nLen
= aDicEvtListeners
.getLength();
1022 bRes
= aDicEvtListeners
.removeInterface( xListener
) != nLen
;
1028 sal_Bool SAL_CALL
DictionaryNeo::hasLocation()
1029 throw(RuntimeException
)
1031 MutexGuard
aGuard( GetLinguMutex() );
1032 return aMainURL
.getLength() > 0;
1035 OUString SAL_CALL
DictionaryNeo::getLocation()
1036 throw(RuntimeException
)
1038 MutexGuard
aGuard( GetLinguMutex() );
1042 sal_Bool SAL_CALL
DictionaryNeo::isReadonly()
1043 throw(RuntimeException
)
1045 MutexGuard
aGuard( GetLinguMutex() );
1050 void SAL_CALL
DictionaryNeo::store()
1051 throw(io::IOException
, RuntimeException
)
1053 MutexGuard
aGuard( GetLinguMutex() );
1055 if (bIsModified
&& hasLocation() && !isReadonly())
1057 if (saveEntries( aMainURL
))
1059 #ifdef LINGU_EXCEPTIONS
1060 throw io::IOException();
1064 bIsModified
= FALSE
;
1068 void SAL_CALL
DictionaryNeo::storeAsURL(
1069 const OUString
& aURL
,
1070 const uno::Sequence
< beans::PropertyValue
>& /*rArgs*/ )
1071 throw(io::IOException
, RuntimeException
)
1073 MutexGuard
aGuard( GetLinguMutex() );
1075 if (saveEntries( aURL
))
1077 #ifdef LINGU_EXCEPTIONS
1078 throw io::IOException();
1084 bIsModified
= FALSE
;
1085 bIsReadonly
= IsReadOnly( getLocation() );
1089 void SAL_CALL
DictionaryNeo::storeToURL(
1090 const OUString
& aURL
,
1091 const uno::Sequence
< beans::PropertyValue
>& /*rArgs*/ )
1092 throw(io::IOException
, RuntimeException
)
1094 MutexGuard
aGuard( GetLinguMutex() );
1096 if (saveEntries( aURL
))
1098 #ifdef LINGU_EXCEPTIONS
1099 throw io::IOException();
1104 ///////////////////////////////////////////////////////////////////////////
1106 DicEntry::DicEntry()
1111 DicEntry::DicEntry(const OUString
&rDicFileWord
,
1112 BOOL bIsNegativWord
)
1114 if (rDicFileWord
.getLength())
1115 splitDicFileWord( rDicFileWord
, aDicWord
, aReplacement
);
1116 bIsNegativ
= bIsNegativWord
;
1119 DicEntry::DicEntry(const OUString
&rDicWord
, BOOL bNegativ
,
1120 const OUString
&rRplcText
) :
1121 aDicWord (rDicWord
),
1122 aReplacement (rRplcText
),
1123 bIsNegativ (bNegativ
)
1127 DicEntry::~DicEntry()
1131 void DicEntry::splitDicFileWord(const OUString
&rDicFileWord
,
1133 OUString
&rReplacement
)
1135 MutexGuard
aGuard( GetLinguMutex() );
1137 static const OUString
aDelim( A2OU( "==" ) );
1139 sal_Int32 nDelimPos
= rDicFileWord
.indexOf( aDelim
);
1140 if (-1 != nDelimPos
)
1142 sal_Int32 nTriplePos
= nDelimPos
+ 2;
1143 if ( nTriplePos
< rDicFileWord
.getLength()
1144 && rDicFileWord
[ nTriplePos
] == '=' )
1146 rDicWord
= rDicFileWord
.copy( 0, nDelimPos
);
1147 rReplacement
= rDicFileWord
.copy( nDelimPos
+ 2 );
1151 rDicWord
= rDicFileWord
;
1152 rReplacement
= OUString();
1156 OUString SAL_CALL
DicEntry::getDictionaryWord( )
1157 throw(RuntimeException
)
1159 MutexGuard
aGuard( GetLinguMutex() );
1163 sal_Bool SAL_CALL
DicEntry::isNegative( )
1164 throw(RuntimeException
)
1166 MutexGuard
aGuard( GetLinguMutex() );
1170 OUString SAL_CALL
DicEntry::getReplacementText( )
1171 throw(RuntimeException
)
1173 MutexGuard
aGuard( GetLinguMutex() );
1174 return aReplacement
;
1178 ///////////////////////////////////////////////////////////////////////////