Update ooo320-m1
[ooovba.git] / linguistic / source / dicimp.cxx
blobe894e7e80cd48f8085ed3631fb51407492a77621
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: dicimp.cxx,v $
10 * $Revision: 1.25 $
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>
35 #ifndef _DICIMP_HXX
36 #include <dicimp.hxx>
37 #endif
38 #ifndef _HYPHIMP_HXX
39 #include <hyphdsp.hxx>
40 #endif
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
57 using namespace utl;
58 using namespace osl;
59 using namespace rtl;
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 ///////////////////////////////////////////////////////////////////////////
68 #define BUFSIZE 4096
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)
84 return FALSE;
86 rTagValue = rLine.Copy( nPos + sal::static_int_cast< xub_StrLen >(strlen( pTagName )) ).EraseLeadingAndTrailingChars();
87 return TRUE;
91 INT16 ReadDicVersion( SvStream *pStream, USHORT &nLng, BOOL &bNeg )
93 // Sniff the header
94 INT16 nDicVersion;
95 sal_Char pMagicHeader[MAX_HEADER_LENGTH];
97 nLng = LANGUAGE_NONE;
98 bNeg = FALSE;
100 if (!pStream || pStream->GetError())
101 return -1;
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))
109 sal_Bool bSuccess;
110 ByteString aLine;
112 nDicVersion = 7;
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
123 continue;
125 // lang: field
126 if (getTag(aLine, "lang: ", aTagValue))
128 if (aTagValue == "<none>")
129 nLng = LANGUAGE_NONE;
130 else
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")
139 bNeg = TRUE;
140 else
141 bNeg = FALSE;
144 if (aLine.Search ("---") != STRING_NOTFOUND) // end of header
145 break;
147 if (!bSuccess)
148 return -2;
150 else
152 USHORT nLen;
154 pStream->Seek (nSniffPos );
156 *pStream >> nLen;
157 if (nLen >= MAX_HEADER_LENGTH)
158 return -1;
160 pStream->Read(pMagicHeader, nLen);
161 pMagicHeader[nLen] = '\0';
163 // Check version magic
164 if (0 == strcmp( pMagicHeader, pVerStr6 ))
165 nDicVersion = 6;
166 else if (0 == strcmp( pMagicHeader, pVerStr5 ))
167 nDicVersion = 5;
168 else if (0 == strcmp( pMagicHeader, pVerStr2 ))
169 nDicVersion = 2;
170 else
171 nDicVersion = -1;
173 if (2 == nDicVersion ||
174 5 == nDicVersion ||
175 6 == nDicVersion)
177 // The language of the dictionary
178 *pStream >> nLng;
180 if (VERS2_NOLANGUAGE == nLng)
181 nLng = LANGUAGE_NONE;
183 // Negative Flag
184 sal_Char nTmp;
185 *pStream >> nTmp;
186 bNeg = (BOOL)nTmp;
190 return nDicVersion;
195 const String GetDicExtension()
197 return String::CreateFromAscii( pDicExt );
200 ///////////////////////////////////////////////////////////////////////////
202 DictionaryNeo::DictionaryNeo() :
203 aDicEvtListeners( GetLinguMutex() ),
204 eDicType (DictionaryType_POSITIVE),
205 nLanguage (LANGUAGE_NONE)
207 nCount = 0;
208 nDicVersion = -1;
209 bNeedEntries = FALSE;
210 bIsModified = bIsActive = FALSE;
211 bIsReadonly = FALSE;
214 DictionaryNeo::DictionaryNeo(const OUString &rName,
215 INT16 nLang, DictionaryType eType,
216 const OUString &rMainURL,
217 BOOL bWriteable) :
218 aDicEvtListeners( GetLinguMutex() ),
219 aDicName (rName),
220 aMainURL (rMainURL),
221 eDicType (eType),
222 nLanguage (nLang)
224 nCount = 0;
225 nDicVersion = -1;
226 bNeedEntries = TRUE;
227 bIsModified = bIsActive = FALSE;
228 bIsReadonly = !bWriteable;
230 if( rMainURL.getLength() > 0 )
232 BOOL bExists = FileExists( rMainURL );
233 if( !bExists )
235 // save new dictionaries with in 6.0 Format (uses UTF8)
236 nDicVersion = 6;
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" );
243 if (!bIsReadonly)
244 saveEntries( rMainURL );
245 bNeedEntries = FALSE;
248 else
250 // non persistent dictionaries (like IgnoreAllList) should always be writable
251 bIsReadonly = FALSE;
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)
272 return 0;
274 ULONG nErr = sal::static_int_cast< ULONG >(-1);
276 // get stream to use
277 SfxMedium aMedium( rMainURL, STREAM_READ | STREAM_SHARE_DENYWRITE, FALSE );
278 SvStream *pStream = aMedium.GetInStream();
279 if (!pStream)
280 return nErr;
282 // Header einlesen
283 BOOL bNegativ;
284 USHORT nLang;
285 nDicVersion = ReadDicVersion(pStream, nLang, bNegativ);
286 if (0 != (nErr = pStream->GetError()))
287 return nErr;
288 nLanguage = nLang;
290 eDicType = bNegativ ? DictionaryType_NEGATIVE : DictionaryType_POSITIVE;
292 rtl_TextEncoding eEnc = osl_getThreadTextEncoding();
293 if (nDicVersion >= 6)
294 eEnc = RTL_TEXTENCODING_UTF8;
295 nCount = 0;
297 if (6 == nDicVersion ||
298 5 == nDicVersion ||
299 2 == nDicVersion)
301 USHORT nLen = 0;
302 sal_Char aWordBuf[ BUFSIZE ];
304 // Das erste Wort einlesen
305 if (!pStream->IsEof())
307 *pStream >> nLen;
308 if (0 != (nErr = pStream->GetError()))
309 return nErr;
310 if ( nLen < BUFSIZE )
312 pStream->Read(aWordBuf, nLen);
313 if (0 != (nErr = pStream->GetError()))
314 return nErr;
315 *(aWordBuf + nLen) = 0;
319 while(!pStream->IsEof())
321 // Aus dem File einlesen
322 // Einfuegen ins Woerterbuch ohne Konvertierung
323 if(*aWordBuf)
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
332 *pStream >> nLen;
333 if (pStream->IsEof()) // #75082# GPF in online-spelling
334 break;
335 if (0 != (nErr = pStream->GetError()))
336 return nErr;
337 #ifdef LINGU_EXCEPTIONS
338 if (nLen >= BUFSIZE)
339 throw io::IOException() ;
340 #endif
342 if (nLen < BUFSIZE)
344 pStream->Read(aWordBuf, nLen);
345 if (0 != (nErr = pStream->GetError()))
346 return nErr;
348 else
349 return SVSTREAM_READ_ERROR;
350 *(aWordBuf + nLen) = 0;
353 else if (7 == nDicVersion)
355 sal_Bool bSuccess;
356 ByteString aLine;
358 // remaining lines - stock strings (a [==] b)
359 while (sal_True == (bSuccess = pStream->ReadLine(aLine)))
361 if (aLine.GetChar(0) == '#') // skip comments
362 continue;
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
375 bIsModified = FALSE;
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())
388 aStr += "==";
389 aStr += ByteString(xEntry->getReplacementText().getStr(), eEnc);
391 return aStr;
395 ULONG DictionaryNeo::saveEntries(const OUString &rURL)
397 MutexGuard aGuard( GetLinguMutex() );
399 if (rURL.getLength() == 0)
400 return 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,
406 FALSE );
407 aMedium.CreateTempFile(); // use temp file to write to...
408 SvStream *pStream = aMedium.GetOutStream();
409 if (!pStream)
410 return nErr;
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()))
420 return nErr;
422 if (nLanguage == LANGUAGE_NONE)
423 pStream->WriteLine(ByteString("lang: <none>"));
424 else
426 ByteString aLine("lang: ");
427 aLine += ByteString( String( MsLangId::convertLanguageToIsoString( nLanguage ) ), eEnc);
428 pStream->WriteLine( aLine );
430 if (0 != (nErr = pStream->GetError()))
431 return nErr;
433 if (eDicType == DictionaryType_POSITIVE)
434 pStream->WriteLine(ByteString("type: positive"));
435 else
436 pStream->WriteLine(ByteString("type: negative"));
437 if (0 != (nErr = pStream->GetError()))
438 return nErr;
440 pStream->WriteLine(ByteString("---"));
441 if (0 != (nErr = pStream->GetError()))
442 return nErr;
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()))
450 return nErr;
453 else
455 sal_Char aWordBuf[BUFSIZE];
457 // write version
458 const sal_Char *pVerStr = NULL;
459 if (6 == nDicVersion)
460 pVerStr = pVerStr6;
461 else
462 pVerStr = eDicType == DictionaryType_POSITIVE ? pVerStr2 : pVerStr5;
463 strcpy( aWordBuf, pVerStr ); // #100211# - checked
464 USHORT nLen = sal::static_int_cast< USHORT >(strlen( aWordBuf ));
465 *pStream << nLen;
466 if (0 != (nErr = pStream->GetError()))
467 return nErr;
468 pStream->Write(aWordBuf, nLen);
469 if (0 != (nErr = pStream->GetError()))
470 return nErr;
472 *pStream << nLanguage;
473 if (0 != (nErr = pStream->GetError()))
474 return nErr;
475 *pStream << (sal_Char) (eDicType == DictionaryType_NEGATIVE ? TRUE : FALSE);
476 if (0 != (nErr = pStream->GetError()))
477 return nErr;
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();
488 if (nLen >= BUFSIZE)
489 nLen = BUFSIZE - 1;
491 *pStream << nLen;
492 if (0 != (nErr = pStream->GetError()))
493 return nErr;
494 pStream->Write(aOutStr.GetBuffer(), nLen);
495 if (0 != (nErr = pStream->GetError()))
496 return nErr;
500 //! get return value before Stream is destroyed
501 ULONG nError = pStream->GetError();
503 // flush file, close it and release any lock
504 aMedium.Close();
505 aMedium.Commit();
507 return nError;
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 );
524 if (xRef.is())
525 xRef->processDictionaryEvent( aEvt );
529 int DictionaryNeo::cmpDicEntry(const OUString& rWord1,
530 const OUString &rWord2,
531 BOOL bSimilarOnly)
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
539 int nRes = 0;
541 OUString aWord1( rWord1 ),
542 aWord2( rWord2 );
543 INT32 nLen1 = aWord1.getLength(),
544 nLen2 = aWord2.getLength();
545 if (bSimilarOnly)
547 const sal_Unicode cChar = '.';
548 if (nLen1 && cChar == aWord1[ nLen1 - 1 ])
549 nLen1--;
550 if (nLen2 && cChar == aWord2[ nLen2 - 1 ])
551 nLen2--;
554 const sal_Unicode cIgnChar = '=';
555 INT32 nIdx1 = 0,
556 nIdx2 = 0,
557 nNumIgnChar1 = 0,
558 nNumIgnChar2 = 0;
560 sal_Int32 nDiff = 0;
561 sal_Unicode cChar1 = '\0';
562 sal_Unicode cChar2 = '\0';
565 // skip chars to be ignored
566 while (nIdx1 < nLen1 && (cChar1 = aWord1[ nIdx1 ]) == cIgnChar)
568 nIdx1++;
569 nNumIgnChar1++;
571 while (nIdx2 < nLen2 && (cChar2 = aWord2[ nIdx2 ]) == cIgnChar)
573 nIdx2++;
574 nNumIgnChar2++;
577 if (nIdx1 < nLen1 && nIdx2 < nLen2)
579 nDiff = cChar1 - cChar2;
580 if (nDiff)
581 break;
582 nIdx1++;
583 nIdx2++;
585 } while (nIdx1 < nLen1 && nIdx2 < nLen2);
588 if (nDiff)
589 nRes = nDiff;
590 else
591 { // the string with the smallest count of not ignored chars is the
592 // shorter one
594 // count remaining IgnChars
595 while (nIdx1 < nLen1 )
597 if (aWord1[ nIdx1++ ] == cIgnChar)
598 nNumIgnChar1++;
600 while (nIdx2 < nLen2 )
602 if (aWord2[ nIdx2++ ] == cIgnChar)
603 nNumIgnChar2++;
606 nRes = ((INT32) nLen1 - nNumIgnChar1) - ((INT32) nLen2 - nNumIgnChar2);
609 return nRes;
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(),
625 nMidIdx,
626 nLowerIdx = 0;
627 if( nUpperIdx > 0 )
629 nUpperIdx--;
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 );
637 if(nCmp == 0)
639 if( pPos ) *pPos = nMidIdx;
640 return TRUE;
642 else if(nCmp > 0)
643 nLowerIdx = nMidIdx + 1;
644 else if( nMidIdx == 0 )
646 if( pPos ) *pPos = nLowerIdx;
647 return FALSE;
649 else
650 nUpperIdx = nMidIdx - 1;
653 if( pPos ) *pPos = nLowerIdx;
654 return FALSE;
657 BOOL DictionaryNeo::isSorted()
659 BOOL bRes = TRUE;
661 const uno::Reference< XDictionaryEntry > *pEntry = aEntries.getConstArray();
662 INT32 nEntries = getCount();
663 INT32 i;
664 for (i = 1; i < nEntries; i++)
666 if (cmpDicEntry( pEntry[i-1]->getDictionaryWord(),
667 pEntry[i]->getDictionaryWord() ) > 0)
669 bRes = FALSE;
670 break;
673 return bRes;
676 BOOL DictionaryNeo::addEntry_Impl(const uno::Reference< XDictionaryEntry > xDicEntry,
677 BOOL bIsLoadEntries)
679 MutexGuard aGuard( GetLinguMutex() );
681 BOOL bRes = FALSE;
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
693 INT32 nPos = 0;
694 BOOL bFound = FALSE;
695 if (bAddEntry)
697 bFound = seekEntry( xDicEntry->getDictionaryWord(), &nPos );
698 if (bFound)
699 bAddEntry = FALSE;
702 if (bAddEntry)
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
711 INT32 i;
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");
718 nCount++;
720 bIsModified = TRUE;
721 bRes = TRUE;
723 if (!bIsLoadEntries)
724 launchEvent( DictionaryEventFlags::ADD_ENTRY, xDicEntry );
728 return bRes;
732 uno::Reference< XInterface > SAL_CALL DictionaryNeo_CreateInstance(
733 const uno::Reference< XMultiServiceFactory > & /*rSMgr*/ )
734 throw(Exception)
736 uno::Reference< XInterface > xService =
737 (cppu::OWeakObject*) new DictionaryNeo;
738 return xService;
741 OUString SAL_CALL DictionaryNeo::getName( )
742 throw(RuntimeException)
744 MutexGuard aGuard( GetLinguMutex() );
745 return aDicName;
748 void SAL_CALL DictionaryNeo::setName( const OUString& aName )
749 throw(RuntimeException)
751 MutexGuard aGuard( GetLinguMutex() );
753 if (aDicName != aName)
755 aDicName = aName;
756 launchEvent(DictionaryEventFlags::CHG_NAME, NULL);
760 DictionaryType SAL_CALL DictionaryNeo::getDictionaryType( )
761 throw(RuntimeException)
763 MutexGuard aGuard( GetLinguMutex() );
765 return eDicType;
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())
787 store();
789 aEntries.realloc( 0 );
790 nCount = 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() );
805 return bIsActive;
808 sal_Int32 SAL_CALL DictionaryNeo::getCount( )
809 throw(RuntimeException)
811 MutexGuard aGuard( GetLinguMutex() );
813 if (bNeedEntries)
814 loadEntries( aMainURL );
815 return nCount;
818 Locale SAL_CALL DictionaryNeo::getLocale( )
819 throw(RuntimeException)
821 MutexGuard aGuard( GetLinguMutex() );
822 Locale aRes;
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() );
846 if (bNeedEntries)
847 loadEntries( aMainURL );
849 INT32 nPos;
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() );
864 BOOL bRes = FALSE;
866 if (!bIsReadonly)
868 if (bNeedEntries)
869 loadEntries( aMainURL );
870 bRes = addEntry_Impl( xDicEntry );
873 return bRes;
876 sal_Bool SAL_CALL
877 DictionaryNeo::add( const OUString& rWord, sal_Bool bIsNegative,
878 const OUString& rRplcText )
879 throw(RuntimeException)
881 MutexGuard aGuard( GetLinguMutex() );
883 BOOL bRes = FALSE;
885 if (!bIsReadonly)
887 uno::Reference< XDictionaryEntry > xEntry =
888 new DicEntry( rWord, bIsNegative, rRplcText );
889 bRes = addEntry_Impl( xEntry );
892 return bRes;
895 void lcl_SequenceRemoveElementAt(
896 uno::Sequence< uno::Reference< XDictionaryEntry > >& rEntries, int nPos )
898 //TODO: helper for SequenceRemoveElementAt available?
899 if(nPos >= rEntries.getLength())
900 return;
901 uno::Sequence< uno::Reference< XDictionaryEntry > > aTmp(rEntries.getLength() - 1);
902 uno::Reference< XDictionaryEntry > * pOrig = rEntries.getArray();
903 uno::Reference< XDictionaryEntry > * pTemp = aTmp.getArray();
904 int nOffset = 0;
905 for(int i = 0; i < aTmp.getLength(); i++)
907 if(nPos == i)
908 nOffset++;
909 pTemp[i] = pOrig[i + nOffset];
912 rEntries = aTmp;
915 sal_Bool SAL_CALL DictionaryNeo::remove( const OUString& aWord )
916 throw(RuntimeException)
918 MutexGuard aGuard( GetLinguMutex() );
920 BOOL bRemoved = FALSE;
922 if (!bIsReadonly)
924 if (bNeedEntries)
925 loadEntries( aMainURL );
927 INT32 nPos;
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
934 if (bFound)
936 // entry to be removed
937 uno::Reference< XDictionaryEntry >
938 xDicEntry( aEntries.getConstArray()[ nPos ] );
939 DBG_ASSERT(xDicEntry.is(), "lng : dictionary entry is NULL");
941 nCount--;
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 );
951 return bRemoved;
954 sal_Bool SAL_CALL DictionaryNeo::isFull( )
955 throw(RuntimeException)
957 MutexGuard aGuard( GetLinguMutex() );
959 if (bNeedEntries)
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() );
970 if (bNeedEntries)
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 );
989 nCount = 0;
990 bNeedEntries = FALSE;
991 bIsModified = TRUE;
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() );
1003 BOOL bRes = FALSE;
1004 if (xListener.is())
1006 INT32 nLen = aDicEvtListeners.getLength();
1007 bRes = aDicEvtListeners.addInterface( xListener ) != nLen;
1009 return bRes;
1012 sal_Bool SAL_CALL DictionaryNeo::removeDictionaryEventListener(
1013 const uno::Reference< XDictionaryEventListener >& xListener )
1014 throw(RuntimeException)
1016 MutexGuard aGuard( GetLinguMutex() );
1018 BOOL bRes = FALSE;
1019 if (xListener.is())
1021 INT32 nLen = aDicEvtListeners.getLength();
1022 bRes = aDicEvtListeners.removeInterface( xListener ) != nLen;
1024 return bRes;
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() );
1039 return aMainURL;
1042 sal_Bool SAL_CALL DictionaryNeo::isReadonly()
1043 throw(RuntimeException)
1045 MutexGuard aGuard( GetLinguMutex() );
1047 return bIsReadonly;
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();
1061 #endif
1063 else
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();
1079 #endif
1081 else
1083 aMainURL = aURL;
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();
1100 #endif
1104 ///////////////////////////////////////////////////////////////////////////
1106 DicEntry::DicEntry()
1108 bIsNegativ = FALSE;
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,
1132 OUString &rDicWord,
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 ] == '=' )
1145 ++nDelimPos;
1146 rDicWord = rDicFileWord.copy( 0, nDelimPos );
1147 rReplacement = rDicFileWord.copy( nDelimPos + 2 );
1149 else
1151 rDicWord = rDicFileWord;
1152 rReplacement = OUString();
1156 OUString SAL_CALL DicEntry::getDictionaryWord( )
1157 throw(RuntimeException)
1159 MutexGuard aGuard( GetLinguMutex() );
1160 return aDicWord;
1163 sal_Bool SAL_CALL DicEntry::isNegative( )
1164 throw(RuntimeException)
1166 MutexGuard aGuard( GetLinguMutex() );
1167 return bIsNegativ;
1170 OUString SAL_CALL DicEntry::getReplacementText( )
1171 throw(RuntimeException)
1173 MutexGuard aGuard( GetLinguMutex() );
1174 return aReplacement;
1178 ///////////////////////////////////////////////////////////////////////////