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_linguistic.hxx"
26
27 #include <cppuhelper/factory.hxx>
28 #include <dicimp.hxx>
29 #include <hyphdsp.hxx>
30 #include <i18npool/lang.h>
31 #include <i18npool/mslangid.hxx>
32 #include <osl/mutex.hxx>
33 #include <tools/debug.hxx>
34 #include <tools/fsys.hxx>
35 #include <tools/stream.hxx>
36 #include <tools/string.hxx>
37 #include <tools/urlobj.hxx>
38 #include <unotools/processfactory.hxx>
39 #include <unotools/ucbstreamhelper.hxx>
40
41 #include <com/sun/star/ucb/XSimpleFileAccess.hpp>
42 #include <com/sun/star/linguistic2/DictionaryType.hpp>
43 #include <com/sun/star/linguistic2/DictionaryEventFlags.hpp>
44 #include <com/sun/star/registry/XRegistryKey.hpp>
45 #include <com/sun/star/io/XInputStream.hpp>
46 #include <com/sun/star/io/XOutputStream.hpp>
47
48 #include "defs.hxx"
49
50
51 using namespace utl;
52 using namespace osl;
53 using namespace rtl;
54 using namespace com::sun::star;
55 using namespace com::sun::star::lang;
56 using namespace com::sun::star::uno;
57 using namespace com::sun::star::linguistic2;
58 using namespace linguistic;
59
60 ///////////////////////////////////////////////////////////////////////////
61
62 #define BUFSIZE 4096
63 #define VERS2_NOLANGUAGE 1024
64
65 #define MAX_HEADER_LENGTH 16
66
67 static const sal_Char* pDicExt = "dic";
68 static const sal_Char* pVerStr2 = "WBSWG2";
69 static const sal_Char* pVerStr5 = "WBSWG5";
70 static const sal_Char* pVerStr6 = "WBSWG6";
71 static const sal_Char* pVerOOo7 = "OOoUserDict1";
72
73 static const sal_Int16 DIC_VERSION_DONTKNOW = -1;
74 static const sal_Int16 DIC_VERSION_2 = 2;
75 static const sal_Int16 DIC_VERSION_5 = 5;
76 static const sal_Int16 DIC_VERSION_6 = 6;
77 static const sal_Int16 DIC_VERSION_7 = 7;
78
getTag(const ByteString & rLine,const sal_Char * pTagName,ByteString & rTagValue)79 static sal_Bool getTag(const ByteString &rLine,
80 const sal_Char *pTagName, ByteString &rTagValue)
81 {
82 xub_StrLen nPos = rLine.Search( pTagName );
83 if (nPos == STRING_NOTFOUND)
84 return sal_False;
85
86 rTagValue = rLine.Copy( nPos + sal::static_int_cast< xub_StrLen >(strlen( pTagName )) ).EraseLeadingAndTrailingChars();
87 return sal_True;
88 }
89
90
ReadDicVersion(SvStreamPtr & rpStream,sal_uInt16 & nLng,sal_Bool & bNeg)91 sal_Int16 ReadDicVersion( SvStreamPtr &rpStream, sal_uInt16 &nLng, sal_Bool &bNeg )
92 {
93 // Sniff the header
94 sal_Int16 nDicVersion = DIC_VERSION_DONTKNOW;
95 sal_Char pMagicHeader[MAX_HEADER_LENGTH];
96
97 nLng = LANGUAGE_NONE;
98 bNeg = sal_False;
99
100 if (!rpStream.get() || rpStream->GetError())
101 return -1;
102
103 sal_Size nSniffPos = rpStream->Tell();
104 static sal_Size nVerOOo7Len = sal::static_int_cast< sal_Size >(strlen( pVerOOo7 ));
105 pMagicHeader[ nVerOOo7Len ] = '\0';
106 if ((rpStream->Read((void *) pMagicHeader, nVerOOo7Len) == nVerOOo7Len) &&
107 !strcmp(pMagicHeader, pVerOOo7))
108 {
109 sal_Bool bSuccess;
110 ByteString aLine;
111
112 nDicVersion = DIC_VERSION_7;
113
114 // 1st skip magic / header line
115 rpStream->ReadLine(aLine);
116
117 // 2nd line: language all | en-US | pt-BR ...
118 while (sal_True == (bSuccess = rpStream->ReadLine(aLine)))
119 {
120 ByteString aTagValue;
121
122 if (aLine.GetChar(0) == '#') // skip comments
123 continue;
124
125 // lang: field
126 if (getTag(aLine, "lang: ", aTagValue))
127 {
128 if (aTagValue == "<none>")
129 nLng = LANGUAGE_NONE;
130 else
131 nLng = MsLangId::convertIsoStringToLanguage(OUString(aTagValue.GetBuffer(),
132 aTagValue.Len(), RTL_TEXTENCODING_ASCII_US));
133 }
134
135 // type: negative / positive
136 if (getTag(aLine, "type: ", aTagValue))
137 {
138 if (aTagValue == "negative")
139 bNeg = sal_True;
140 else
141 bNeg = sal_False;
142 }
143
144 if (aLine.Search ("---") != STRING_NOTFOUND) // end of header
145 break;
146 }
147 if (!bSuccess)
148 return -2;
149 }
150 else
151 {
152 sal_uInt16 nLen;
153
154 rpStream->Seek (nSniffPos );
155
156 *rpStream >> nLen;
157 if (nLen >= MAX_HEADER_LENGTH)
158 return -1;
159
160 rpStream->Read(pMagicHeader, nLen);
161 pMagicHeader[nLen] = '\0';
162
163 // Check version magic
164 if (0 == strcmp( pMagicHeader, pVerStr6 ))
165 nDicVersion = DIC_VERSION_6;
166 else if (0 == strcmp( pMagicHeader, pVerStr5 ))
167 nDicVersion = DIC_VERSION_5;
168 else if (0 == strcmp( pMagicHeader, pVerStr2 ))
169 nDicVersion = DIC_VERSION_2;
170 else
171 nDicVersion = DIC_VERSION_DONTKNOW;
172
173 if (DIC_VERSION_2 == nDicVersion ||
174 DIC_VERSION_5 == nDicVersion ||
175 DIC_VERSION_6 == nDicVersion)
176 {
177 // The language of the dictionary
178 *rpStream >> nLng;
179
180 if (VERS2_NOLANGUAGE == nLng)
181 nLng = LANGUAGE_NONE;
182
183 // Negative Flag
184 sal_Char nTmp;
185 *rpStream >> nTmp;
186 bNeg = (sal_Bool)nTmp;
187 }
188 }
189
190 return nDicVersion;
191 }
192
193
194
GetDicExtension()195 const String GetDicExtension()
196 {
197 return String::CreateFromAscii( pDicExt );
198 }
199
200 ///////////////////////////////////////////////////////////////////////////
201
DictionaryNeo()202 DictionaryNeo::DictionaryNeo() :
203 aDicEvtListeners( GetLinguMutex() ),
204 eDicType (DictionaryType_POSITIVE),
205 nLanguage (LANGUAGE_NONE)
206 {
207 nCount = 0;
208 nDicVersion = DIC_VERSION_DONTKNOW;
209 bNeedEntries = sal_False;
210 bIsModified = bIsActive = sal_False;
211 bIsReadonly = sal_False;
212 }
213
DictionaryNeo(const OUString & rName,sal_Int16 nLang,DictionaryType eType,const OUString & rMainURL,sal_Bool bWriteable)214 DictionaryNeo::DictionaryNeo(const OUString &rName,
215 sal_Int16 nLang, DictionaryType eType,
216 const OUString &rMainURL,
217 sal_Bool bWriteable) :
218 aDicEvtListeners( GetLinguMutex() ),
219 aDicName (rName),
220 aMainURL (rMainURL),
221 eDicType (eType),
222 nLanguage (nLang)
223 {
224 nCount = 0;
225 nDicVersion = DIC_VERSION_DONTKNOW;
226 bNeedEntries = sal_True;
227 bIsModified = bIsActive = sal_False;
228 bIsReadonly = !bWriteable;
229
230 if( rMainURL.getLength() > 0 )
231 {
232 sal_Bool bExists = FileExists( rMainURL );
233 if( !bExists )
234 {
235 // save new dictionaries with in Format 7 (UTF8 plain text)
236 nDicVersion = DIC_VERSION_7;
237
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 = sal_False;
246 }
247 }
248 else
249 {
250 // non persistent dictionaries (like IgnoreAllList) should always be writable
251 bIsReadonly = sal_False;
252 bNeedEntries = sal_False;
253 }
254 }
255
~DictionaryNeo()256 DictionaryNeo::~DictionaryNeo()
257 {
258 }
259
loadEntries(const OUString & rMainURL)260 sal_uLong DictionaryNeo::loadEntries(const OUString &rMainURL)
261 {
262 MutexGuard aGuard( GetLinguMutex() );
263
264 // counter check that it is safe to set bIsModified to sal_False at
265 // the end of the function
266 DBG_ASSERT(!bIsModified, "lng : dictionary already modified!");
267
268 // function should only be called once in order to load entries from file
269 bNeedEntries = sal_False;
270
271 if (rMainURL.getLength() == 0)
272 return 0;
273
274 uno::Reference< lang::XMultiServiceFactory > xServiceFactory( utl::getProcessServiceFactory() );
275
276 // get XInputStream stream
277 uno::Reference< io::XInputStream > xStream;
278 try
279 {
280 uno::Reference< ucb::XSimpleFileAccess > xAccess( xServiceFactory->createInstance(
281 A2OU( "com.sun.star.ucb.SimpleFileAccess" ) ), uno::UNO_QUERY_THROW );
282 xStream = xAccess->openFileRead( rMainURL );
283 }
284 catch (uno::Exception & e)
285 {
286 DBG_ASSERT( 0, "failed to get input stream" );
287 (void) e;
288 }
289 if (!xStream.is())
290 return static_cast< sal_uLong >(-1);
291
292 SvStreamPtr pStream = SvStreamPtr( utl::UcbStreamHelper::CreateStream( xStream ) );
293
294 sal_uLong nErr = sal::static_int_cast< sal_uLong >(-1);
295
296 // Header einlesen
297 sal_Bool bNegativ;
298 sal_uInt16 nLang;
299 nDicVersion = ReadDicVersion(pStream, nLang, bNegativ);
300 if (0 != (nErr = pStream->GetError()))
301 return nErr;
302
303 nLanguage = nLang;
304
305 eDicType = bNegativ ? DictionaryType_NEGATIVE : DictionaryType_POSITIVE;
306
307 rtl_TextEncoding eEnc = osl_getThreadTextEncoding();
308 if (nDicVersion >= DIC_VERSION_6)
309 eEnc = RTL_TEXTENCODING_UTF8;
310 nCount = 0;
311
312 if (DIC_VERSION_6 == nDicVersion ||
313 DIC_VERSION_5 == nDicVersion ||
314 DIC_VERSION_2 == nDicVersion)
315 {
316 sal_uInt16 nLen = 0;
317 sal_Char aWordBuf[ BUFSIZE ];
318
319 // Das erste Wort einlesen
320 if (!pStream->IsEof())
321 {
322 *pStream >> nLen;
323 if (0 != (nErr = pStream->GetError()))
324 return nErr;
325 if ( nLen < BUFSIZE )
326 {
327 pStream->Read(aWordBuf, nLen);
328 if (0 != (nErr = pStream->GetError()))
329 return nErr;
330 *(aWordBuf + nLen) = 0;
331 }
332 }
333
334 while(!pStream->IsEof())
335 {
336 // Aus dem File einlesen
337 // Einfuegen ins Woerterbuch ohne Konvertierung
338 if(*aWordBuf)
339 {
340 ByteString aDummy( aWordBuf );
341 String aText( aDummy, eEnc );
342 uno::Reference< XDictionaryEntry > xEntry =
343 new DicEntry( aText, bNegativ );
344 addEntry_Impl( xEntry , sal_True ); //! don't launch events here
345 }
346
347 *pStream >> nLen;
348 if (pStream->IsEof()) // #75082# GPF in online-spelling
349 break;
350 if (0 != (nErr = pStream->GetError()))
351 return nErr;
352 #ifdef LINGU_EXCEPTIONS
353 if (nLen >= BUFSIZE)
354 throw io::IOException() ;
355 #endif
356
357 if (nLen < BUFSIZE)
358 {
359 pStream->Read(aWordBuf, nLen);
360 if (0 != (nErr = pStream->GetError()))
361 return nErr;
362 }
363 else
364 return SVSTREAM_READ_ERROR;
365 *(aWordBuf + nLen) = 0;
366 }
367 }
368 else if (DIC_VERSION_7 == nDicVersion)
369 {
370 sal_Bool bSuccess;
371 ByteString aLine;
372
373 // remaining lines - stock strings (a [==] b)
374 while (sal_True == (bSuccess = pStream->ReadLine(aLine)))
375 {
376 if (aLine.GetChar(0) == '#') // skip comments
377 continue;
378 rtl::OUString aText = rtl::OStringToOUString (aLine, RTL_TEXTENCODING_UTF8);
379 uno::Reference< XDictionaryEntry > xEntry =
380 new DicEntry( aText, eDicType == DictionaryType_NEGATIVE );
381 addEntry_Impl( xEntry , sal_True ); //! don't launch events here
382 }
383 }
384
385 DBG_ASSERT(isSorted(), "lng : dictionary is not sorted");
386
387 // since this routine should be called only initialy (prior to any
388 // modification to be saved) we reset the bIsModified flag here that
389 // was implicitly set by addEntry_Impl
390 bIsModified = sal_False;
391
392 return pStream->GetError();
393 }
394
395
formatForSave(const uno::Reference<XDictionaryEntry> & xEntry,rtl_TextEncoding eEnc)396 static ByteString formatForSave(
397 const uno::Reference< XDictionaryEntry > &xEntry, rtl_TextEncoding eEnc )
398 {
399 ByteString aStr(xEntry->getDictionaryWord().getStr(), eEnc);
400
401 if (xEntry->isNegative())
402 {
403 aStr += "==";
404 aStr += ByteString(xEntry->getReplacementText().getStr(), eEnc);
405 }
406 return aStr;
407 }
408
409
saveEntries(const OUString & rURL)410 sal_uLong DictionaryNeo::saveEntries(const OUString &rURL)
411 {
412 MutexGuard aGuard( GetLinguMutex() );
413
414 if (rURL.getLength() == 0)
415 return 0;
416 DBG_ASSERT(!INetURLObject( rURL ).HasError(), "lng : invalid URL");
417
418 uno::Reference< lang::XMultiServiceFactory > xServiceFactory( utl::getProcessServiceFactory() );
419
420 // get XOutputStream stream
421 uno::Reference< io::XStream > xStream;
422 try
423 {
424 uno::Reference< ucb::XSimpleFileAccess > xAccess( xServiceFactory->createInstance(
425 A2OU( "com.sun.star.ucb.SimpleFileAccess" ) ), uno::UNO_QUERY_THROW );
426 xStream = xAccess->openFileReadWrite( rURL );
427 }
428 catch (uno::Exception & e)
429 {
430 DBG_ASSERT( 0, "failed to get input stream" );
431 (void) e;
432 }
433 if (!xStream.is())
434 return static_cast< sal_uLong >(-1);
435
436 SvStreamPtr pStream = SvStreamPtr( utl::UcbStreamHelper::CreateStream( xStream ) );
437 sal_uLong nErr = sal::static_int_cast< sal_uLong >(-1);
438
439 //
440 // Always write as the latest version, i.e. DIC_VERSION_7
441 //
442 rtl_TextEncoding eEnc = RTL_TEXTENCODING_UTF8;
443 pStream->WriteLine(ByteString (pVerOOo7));
444 if (0 != (nErr = pStream->GetError()))
445 return nErr;
446 if (nLanguage == LANGUAGE_NONE)
447 pStream->WriteLine(ByteString("lang: <none>"));
448 else
449 {
450 ByteString aLine("lang: ");
451 aLine += ByteString( String( MsLangId::convertLanguageToIsoString( nLanguage ) ), eEnc);
452 pStream->WriteLine( aLine );
453 }
454 if (0 != (nErr = pStream->GetError()))
455 return nErr;
456 if (eDicType == DictionaryType_POSITIVE)
457 pStream->WriteLine(ByteString("type: positive"));
458 else
459 pStream->WriteLine(ByteString("type: negative"));
460 if (0 != (nErr = pStream->GetError()))
461 return nErr;
462 pStream->WriteLine(ByteString("---"));
463 if (0 != (nErr = pStream->GetError()))
464 return nErr;
465 const uno::Reference< XDictionaryEntry > *pEntry = aEntries.getConstArray();
466 for (sal_Int32 i = 0; i < nCount; i++)
467 {
468 ByteString aOutStr = formatForSave(pEntry[i], eEnc);
469 pStream->WriteLine (aOutStr);
470 if (0 != (nErr = pStream->GetError()))
471 return nErr;
472 }
473
474 //If we are migrating from an older version, then on first successful
475 //write, we're now converted to the latest version, i.e. DIC_VERSION_7
476 nDicVersion = DIC_VERSION_7;
477
478 return nErr;
479 }
480
launchEvent(sal_Int16 nEvent,uno::Reference<XDictionaryEntry> xEntry)481 void DictionaryNeo::launchEvent(sal_Int16 nEvent,
482 uno::Reference< XDictionaryEntry > xEntry)
483 {
484 MutexGuard aGuard( GetLinguMutex() );
485
486 DictionaryEvent aEvt;
487 aEvt.Source = uno::Reference< XDictionary >( this );
488 aEvt.nEvent = nEvent;
489 aEvt.xDictionaryEntry = xEntry;
490
491 cppu::OInterfaceIteratorHelper aIt( aDicEvtListeners );
492 while (aIt.hasMoreElements())
493 {
494 uno::Reference< XDictionaryEventListener > xRef( aIt.next(), UNO_QUERY );
495 if (xRef.is())
496 xRef->processDictionaryEvent( aEvt );
497 }
498 }
499
cmpDicEntry(const OUString & rWord1,const OUString & rWord2,sal_Bool bSimilarOnly)500 int DictionaryNeo::cmpDicEntry(const OUString& rWord1,
501 const OUString &rWord2,
502 sal_Bool bSimilarOnly)
503 {
504 MutexGuard aGuard( GetLinguMutex() );
505
506 // returns 0 if rWord1 is equal to rWord2
507 // " a value < 0 if rWord1 is less than rWord2
508 // " a value > 0 if rWord1 is greater than rWord2
509
510 int nRes = 0;
511
512 OUString aWord1( rWord1 ),
513 aWord2( rWord2 );
514 sal_Int32 nLen1 = aWord1.getLength(),
515 nLen2 = aWord2.getLength();
516 if (bSimilarOnly)
517 {
518 const sal_Unicode cChar = '.';
519 if (nLen1 && cChar == aWord1[ nLen1 - 1 ])
520 nLen1--;
521 if (nLen2 && cChar == aWord2[ nLen2 - 1 ])
522 nLen2--;
523 }
524
525 const sal_Unicode cIgnChar = '=';
526 sal_Int32 nIdx1 = 0,
527 nIdx2 = 0,
528 nNumIgnChar1 = 0,
529 nNumIgnChar2 = 0;
530
531 sal_Int32 nDiff = 0;
532 sal_Unicode cChar1 = '\0';
533 sal_Unicode cChar2 = '\0';
534 do
535 {
536 // skip chars to be ignored
537 while (nIdx1 < nLen1 && (cChar1 = aWord1[ nIdx1 ]) == cIgnChar)
538 {
539 nIdx1++;
540 nNumIgnChar1++;
541 }
542 while (nIdx2 < nLen2 && (cChar2 = aWord2[ nIdx2 ]) == cIgnChar)
543 {
544 nIdx2++;
545 nNumIgnChar2++;
546 }
547
548 if (nIdx1 < nLen1 && nIdx2 < nLen2)
549 {
550 nDiff = cChar1 - cChar2;
551 if (nDiff)
552 break;
553 nIdx1++;
554 nIdx2++;
555 }
556 } while (nIdx1 < nLen1 && nIdx2 < nLen2);
557
558
559 if (nDiff)
560 nRes = nDiff;
561 else
562 { // the string with the smallest count of not ignored chars is the
563 // shorter one
564
565 // count remaining IgnChars
566 while (nIdx1 < nLen1 )
567 {
568 if (aWord1[ nIdx1++ ] == cIgnChar)
569 nNumIgnChar1++;
570 }
571 while (nIdx2 < nLen2 )
572 {
573 if (aWord2[ nIdx2++ ] == cIgnChar)
574 nNumIgnChar2++;
575 }
576
577 nRes = ((sal_Int32) nLen1 - nNumIgnChar1) - ((sal_Int32) nLen2 - nNumIgnChar2);
578 }
579
580 return nRes;
581 }
582
seekEntry(const OUString & rWord,sal_Int32 * pPos,sal_Bool bSimilarOnly)583 sal_Bool DictionaryNeo::seekEntry(const OUString &rWord,
584 sal_Int32 *pPos, sal_Bool bSimilarOnly)
585 {
586 // look for entry with binary search.
587 // return sal_True if found sal_False else.
588 // if pPos != NULL it will become the position of the found entry, or
589 // if that was not found the position where it has to be inserted
590 // to keep the entries sorted
591
592 MutexGuard aGuard( GetLinguMutex() );
593
594 const uno::Reference< XDictionaryEntry > *pEntry = aEntries.getConstArray();
595 sal_Int32 nUpperIdx = getCount(),
596 nMidIdx,
597 nLowerIdx = 0;
598 if( nUpperIdx > 0 )
599 {
600 nUpperIdx--;
601 while( nLowerIdx <= nUpperIdx )
602 {
603 nMidIdx = (nLowerIdx + nUpperIdx) / 2;
604 DBG_ASSERT(pEntry[nMidIdx].is(), "lng : empty entry encountered");
605
606 int nCmp = - cmpDicEntry( pEntry[nMidIdx]->getDictionaryWord(),
607 rWord, bSimilarOnly );
608 if(nCmp == 0)
609 {
610 if( pPos ) *pPos = nMidIdx;
611 return sal_True;
612 }
613 else if(nCmp > 0)
614 nLowerIdx = nMidIdx + 1;
615 else if( nMidIdx == 0 )
616 {
617 if( pPos ) *pPos = nLowerIdx;
618 return sal_False;
619 }
620 else
621 nUpperIdx = nMidIdx - 1;
622 }
623 }
624 if( pPos ) *pPos = nLowerIdx;
625 return sal_False;
626 }
627
isSorted()628 sal_Bool DictionaryNeo::isSorted()
629 {
630 sal_Bool bRes = sal_True;
631
632 const uno::Reference< XDictionaryEntry > *pEntry = aEntries.getConstArray();
633 sal_Int32 nEntries = getCount();
634 sal_Int32 i;
635 for (i = 1; i < nEntries; i++)
636 {
637 if (cmpDicEntry( pEntry[i-1]->getDictionaryWord(),
638 pEntry[i]->getDictionaryWord() ) > 0)
639 {
640 bRes = sal_False;
641 break;
642 }
643 }
644 return bRes;
645 }
646
addEntry_Impl(const uno::Reference<XDictionaryEntry> xDicEntry,sal_Bool bIsLoadEntries)647 sal_Bool DictionaryNeo::addEntry_Impl(const uno::Reference< XDictionaryEntry > xDicEntry,
648 sal_Bool bIsLoadEntries)
649 {
650 MutexGuard aGuard( GetLinguMutex() );
651
652 sal_Bool bRes = sal_False;
653
654 if ( bIsLoadEntries || (!bIsReadonly && xDicEntry.is()) )
655 {
656 sal_Bool bIsNegEntry = xDicEntry->isNegative();
657 sal_Bool bAddEntry = !isFull() &&
658 ( ( eDicType == DictionaryType_POSITIVE && !bIsNegEntry )
659 || ( eDicType == DictionaryType_NEGATIVE && bIsNegEntry )
660 || ( eDicType == DictionaryType_MIXED ) );
661
662 // look for position to insert entry at
663 // if there is already an entry do not insert the new one
664 sal_Int32 nPos = 0;
665 sal_Bool bFound = sal_False;
666 if (bAddEntry)
667 {
668 bFound = seekEntry( xDicEntry->getDictionaryWord(), &nPos );
669 if (bFound)
670 bAddEntry = sal_False;
671 }
672
673 if (bAddEntry)
674 {
675 DBG_ASSERT(!bNeedEntries, "lng : entries still not loaded");
676
677 if (nCount >= aEntries.getLength())
678 aEntries.realloc( Max(2 * nCount, nCount + 32) );
679 uno::Reference< XDictionaryEntry > *pEntry = aEntries.getArray();
680
681 // shift old entries right
682 sal_Int32 i;
683 for (i = nCount - 1; i >= nPos; i--)
684 pEntry[ i+1 ] = pEntry[ i ];
685 // insert new entry at specified position
686 pEntry[ nPos ] = xDicEntry;
687 DBG_ASSERT(isSorted(), "lng : dictionary entries unsorted");
688
689 nCount++;
690
691 bIsModified = sal_True;
692 bRes = sal_True;
693
694 if (!bIsLoadEntries)
695 launchEvent( DictionaryEventFlags::ADD_ENTRY, xDicEntry );
696 }
697 }
698
699 return bRes;
700 }
701
702
DictionaryNeo_CreateInstance(const uno::Reference<XMultiServiceFactory> &)703 uno::Reference< XInterface > SAL_CALL DictionaryNeo_CreateInstance(
704 const uno::Reference< XMultiServiceFactory > & /*rSMgr*/ )
705 throw(Exception)
706 {
707 uno::Reference< XInterface > xService =
708 (cppu::OWeakObject*) new DictionaryNeo;
709 return xService;
710 }
711
getName()712 OUString SAL_CALL DictionaryNeo::getName( )
713 throw(RuntimeException)
714 {
715 MutexGuard aGuard( GetLinguMutex() );
716 return aDicName;
717 }
718
setName(const OUString & aName)719 void SAL_CALL DictionaryNeo::setName( const OUString& aName )
720 throw(RuntimeException)
721 {
722 MutexGuard aGuard( GetLinguMutex() );
723
724 if (aDicName != aName)
725 {
726 aDicName = aName;
727 launchEvent(DictionaryEventFlags::CHG_NAME, NULL);
728 }
729 }
730
getDictionaryType()731 DictionaryType SAL_CALL DictionaryNeo::getDictionaryType( )
732 throw(RuntimeException)
733 {
734 MutexGuard aGuard( GetLinguMutex() );
735
736 return eDicType;
737 }
738
setActive(sal_Bool bActivate)739 void SAL_CALL DictionaryNeo::setActive( sal_Bool bActivate )
740 throw(RuntimeException)
741 {
742 MutexGuard aGuard( GetLinguMutex() );
743
744 if (bIsActive != bActivate)
745 {
746 bIsActive = bActivate != 0;
747 sal_Int16 nEvent = bIsActive ?
748 DictionaryEventFlags::ACTIVATE_DIC : DictionaryEventFlags::DEACTIVATE_DIC;
749
750 // remove entries from memory if dictionary is deactivated
751 if (bIsActive == sal_False)
752 {
753 sal_Bool bIsEmpty = nCount == 0;
754
755 // save entries first if necessary
756 if (bIsModified && hasLocation() && !isReadonly())
757 {
758 store();
759
760 aEntries.realloc( 0 );
761 nCount = 0;
762 bNeedEntries = !bIsEmpty;
763 }
764 DBG_ASSERT( !bIsModified || !hasLocation() || isReadonly(),
765 "lng : dictionary is still modified" );
766 }
767
768 launchEvent(nEvent, NULL);
769 }
770 }
771
isActive()772 sal_Bool SAL_CALL DictionaryNeo::isActive( )
773 throw(RuntimeException)
774 {
775 MutexGuard aGuard( GetLinguMutex() );
776 return bIsActive;
777 }
778
getCount()779 sal_Int32 SAL_CALL DictionaryNeo::getCount( )
780 throw(RuntimeException)
781 {
782 MutexGuard aGuard( GetLinguMutex() );
783
784 if (bNeedEntries)
785 loadEntries( aMainURL );
786 return nCount;
787 }
788
getLocale()789 Locale SAL_CALL DictionaryNeo::getLocale( )
790 throw(RuntimeException)
791 {
792 MutexGuard aGuard( GetLinguMutex() );
793 Locale aRes;
794 return LanguageToLocale( aRes, nLanguage );
795 }
796
setLocale(const Locale & aLocale)797 void SAL_CALL DictionaryNeo::setLocale( const Locale& aLocale )
798 throw(RuntimeException)
799 {
800 MutexGuard aGuard( GetLinguMutex() );
801 sal_Int16 nLanguageP = LocaleToLanguage( aLocale );
802 if (!bIsReadonly && nLanguage != nLanguageP)
803 {
804 nLanguage = nLanguageP;
805 bIsModified = sal_True; // new language needs to be saved with dictionary
806
807 launchEvent( DictionaryEventFlags::CHG_LANGUAGE, NULL );
808 }
809 }
810
getEntry(const OUString & aWord)811 uno::Reference< XDictionaryEntry > SAL_CALL DictionaryNeo::getEntry(
812 const OUString& aWord )
813 throw(RuntimeException)
814 {
815 MutexGuard aGuard( GetLinguMutex() );
816
817 if (bNeedEntries)
818 loadEntries( aMainURL );
819
820 sal_Int32 nPos;
821 sal_Bool bFound = seekEntry( aWord, &nPos, sal_True );
822 DBG_ASSERT( nCount <= aEntries.getLength(), "lng : wrong number of entries");
823 DBG_ASSERT(!bFound || nPos < nCount, "lng : index out of range");
824
825 return bFound ? aEntries.getConstArray()[ nPos ]
826 : uno::Reference< XDictionaryEntry >();
827 }
828
addEntry(const uno::Reference<XDictionaryEntry> & xDicEntry)829 sal_Bool SAL_CALL DictionaryNeo::addEntry(
830 const uno::Reference< XDictionaryEntry >& xDicEntry )
831 throw(RuntimeException)
832 {
833 MutexGuard aGuard( GetLinguMutex() );
834
835 sal_Bool bRes = sal_False;
836
837 if (!bIsReadonly)
838 {
839 if (bNeedEntries)
840 loadEntries( aMainURL );
841 bRes = addEntry_Impl( xDicEntry );
842 }
843
844 return bRes;
845 }
846
847 sal_Bool SAL_CALL
add(const OUString & rWord,sal_Bool bIsNegative,const OUString & rRplcText)848 DictionaryNeo::add( const OUString& rWord, sal_Bool bIsNegative,
849 const OUString& rRplcText )
850 throw(RuntimeException)
851 {
852 MutexGuard aGuard( GetLinguMutex() );
853
854 sal_Bool bRes = sal_False;
855
856 if (!bIsReadonly)
857 {
858 uno::Reference< XDictionaryEntry > xEntry =
859 new DicEntry( rWord, bIsNegative, rRplcText );
860 bRes = addEntry_Impl( xEntry );
861 }
862
863 return bRes;
864 }
865
lcl_SequenceRemoveElementAt(uno::Sequence<uno::Reference<XDictionaryEntry>> & rEntries,int nPos)866 void lcl_SequenceRemoveElementAt(
867 uno::Sequence< uno::Reference< XDictionaryEntry > >& rEntries, int nPos )
868 {
869 //TODO: helper for SequenceRemoveElementAt available?
870 if(nPos >= rEntries.getLength())
871 return;
872 uno::Sequence< uno::Reference< XDictionaryEntry > > aTmp(rEntries.getLength() - 1);
873 uno::Reference< XDictionaryEntry > * pOrig = rEntries.getArray();
874 uno::Reference< XDictionaryEntry > * pTemp = aTmp.getArray();
875 int nOffset = 0;
876 for(int i = 0; i < aTmp.getLength(); i++)
877 {
878 if(nPos == i)
879 nOffset++;
880 pTemp[i] = pOrig[i + nOffset];
881 }
882
883 rEntries = aTmp;
884 }
885
remove(const OUString & aWord)886 sal_Bool SAL_CALL DictionaryNeo::remove( const OUString& aWord )
887 throw(RuntimeException)
888 {
889 MutexGuard aGuard( GetLinguMutex() );
890
891 sal_Bool bRemoved = sal_False;
892
893 if (!bIsReadonly)
894 {
895 if (bNeedEntries)
896 loadEntries( aMainURL );
897
898 sal_Int32 nPos;
899 sal_Bool bFound = seekEntry( aWord, &nPos );
900 DBG_ASSERT( nCount < aEntries.getLength(),
901 "lng : wrong number of entries");
902 DBG_ASSERT(!bFound || nPos < nCount, "lng : index out of range");
903
904 // remove element if found
905 if (bFound)
906 {
907 // entry to be removed
908 uno::Reference< XDictionaryEntry >
909 xDicEntry( aEntries.getConstArray()[ nPos ] );
910 DBG_ASSERT(xDicEntry.is(), "lng : dictionary entry is NULL");
911
912 nCount--;
913
914 //! the following call reduces the length of the sequence by 1 also
915 lcl_SequenceRemoveElementAt( aEntries, nPos );
916 bRemoved = bIsModified = sal_True;
917
918 launchEvent( DictionaryEventFlags::DEL_ENTRY, xDicEntry );
919 }
920 }
921
922 return bRemoved;
923 }
924
isFull()925 sal_Bool SAL_CALL DictionaryNeo::isFull( )
926 throw(RuntimeException)
927 {
928 MutexGuard aGuard( GetLinguMutex() );
929
930 if (bNeedEntries)
931 loadEntries( aMainURL );
932 return nCount >= DIC_MAX_ENTRIES;
933 }
934
935 uno::Sequence< uno::Reference< XDictionaryEntry > >
getEntries()936 SAL_CALL DictionaryNeo::getEntries( )
937 throw(RuntimeException)
938 {
939 MutexGuard aGuard( GetLinguMutex() );
940
941 if (bNeedEntries)
942 loadEntries( aMainURL );
943 //! return sequence with length equal to the number of dictionary entries
944 //! (internal used sequence may have additional unused elements.)
945 return uno::Sequence< uno::Reference< XDictionaryEntry > >
946 (aEntries.getConstArray(), nCount);
947 }
948
949
clear()950 void SAL_CALL DictionaryNeo::clear( )
951 throw(RuntimeException)
952 {
953 MutexGuard aGuard( GetLinguMutex() );
954
955 if (!bIsReadonly && nCount)
956 {
957 // release all references to old entries and provide space for new ones
958 aEntries = uno::Sequence< uno::Reference< XDictionaryEntry > > ( 32 );
959
960 nCount = 0;
961 bNeedEntries = sal_False;
962 bIsModified = sal_True;
963
964 launchEvent( DictionaryEventFlags::ENTRIES_CLEARED , NULL );
965 }
966 }
967
addDictionaryEventListener(const uno::Reference<XDictionaryEventListener> & xListener)968 sal_Bool SAL_CALL DictionaryNeo::addDictionaryEventListener(
969 const uno::Reference< XDictionaryEventListener >& xListener )
970 throw(RuntimeException)
971 {
972 MutexGuard aGuard( GetLinguMutex() );
973
974 sal_Bool bRes = sal_False;
975 if (xListener.is())
976 {
977 sal_Int32 nLen = aDicEvtListeners.getLength();
978 bRes = aDicEvtListeners.addInterface( xListener ) != nLen;
979 }
980 return bRes;
981 }
982
removeDictionaryEventListener(const uno::Reference<XDictionaryEventListener> & xListener)983 sal_Bool SAL_CALL DictionaryNeo::removeDictionaryEventListener(
984 const uno::Reference< XDictionaryEventListener >& xListener )
985 throw(RuntimeException)
986 {
987 MutexGuard aGuard( GetLinguMutex() );
988
989 sal_Bool bRes = sal_False;
990 if (xListener.is())
991 {
992 sal_Int32 nLen = aDicEvtListeners.getLength();
993 bRes = aDicEvtListeners.removeInterface( xListener ) != nLen;
994 }
995 return bRes;
996 }
997
998
hasLocation()999 sal_Bool SAL_CALL DictionaryNeo::hasLocation()
1000 throw(RuntimeException)
1001 {
1002 MutexGuard aGuard( GetLinguMutex() );
1003 return aMainURL.getLength() > 0;
1004 }
1005
getLocation()1006 OUString SAL_CALL DictionaryNeo::getLocation()
1007 throw(RuntimeException)
1008 {
1009 MutexGuard aGuard( GetLinguMutex() );
1010 return aMainURL;
1011 }
1012
isReadonly()1013 sal_Bool SAL_CALL DictionaryNeo::isReadonly()
1014 throw(RuntimeException)
1015 {
1016 MutexGuard aGuard( GetLinguMutex() );
1017
1018 return bIsReadonly;
1019 }
1020
store()1021 void SAL_CALL DictionaryNeo::store()
1022 throw(io::IOException, RuntimeException)
1023 {
1024 MutexGuard aGuard( GetLinguMutex() );
1025
1026 if (bIsModified && hasLocation() && !isReadonly())
1027 {
1028 if (saveEntries( aMainURL ))
1029 {
1030 #ifdef LINGU_EXCEPTIONS
1031 throw io::IOException();
1032 #endif
1033 }
1034 else
1035 bIsModified = sal_False;
1036 }
1037 }
1038
storeAsURL(const OUString & aURL,const uno::Sequence<beans::PropertyValue> &)1039 void SAL_CALL DictionaryNeo::storeAsURL(
1040 const OUString& aURL,
1041 const uno::Sequence< beans::PropertyValue >& /*rArgs*/ )
1042 throw(io::IOException, RuntimeException)
1043 {
1044 MutexGuard aGuard( GetLinguMutex() );
1045
1046 if (saveEntries( aURL ))
1047 {
1048 #ifdef LINGU_EXCEPTIONS
1049 throw io::IOException();
1050 #endif
1051 }
1052 else
1053 {
1054 aMainURL = aURL;
1055 bIsModified = sal_False;
1056 bIsReadonly = IsReadOnly( getLocation() );
1057 }
1058 }
1059
storeToURL(const OUString & aURL,const uno::Sequence<beans::PropertyValue> &)1060 void SAL_CALL DictionaryNeo::storeToURL(
1061 const OUString& aURL,
1062 const uno::Sequence< beans::PropertyValue >& /*rArgs*/ )
1063 throw(io::IOException, RuntimeException)
1064 {
1065 MutexGuard aGuard( GetLinguMutex() );
1066
1067 if (saveEntries( aURL ))
1068 {
1069 #ifdef LINGU_EXCEPTIONS
1070 throw io::IOException();
1071 #endif
1072 }
1073 }
1074
1075 ///////////////////////////////////////////////////////////////////////////
1076
DicEntry()1077 DicEntry::DicEntry()
1078 {
1079 bIsNegativ = sal_False;
1080 }
1081
DicEntry(const OUString & rDicFileWord,sal_Bool bIsNegativWord)1082 DicEntry::DicEntry(const OUString &rDicFileWord,
1083 sal_Bool bIsNegativWord)
1084 {
1085 if (rDicFileWord.getLength())
1086 splitDicFileWord( rDicFileWord, aDicWord, aReplacement );
1087 bIsNegativ = bIsNegativWord;
1088 }
1089
DicEntry(const OUString & rDicWord,sal_Bool bNegativ,const OUString & rRplcText)1090 DicEntry::DicEntry(const OUString &rDicWord, sal_Bool bNegativ,
1091 const OUString &rRplcText) :
1092 aDicWord (rDicWord),
1093 aReplacement (rRplcText),
1094 bIsNegativ (bNegativ)
1095 {
1096 }
1097
~DicEntry()1098 DicEntry::~DicEntry()
1099 {
1100 }
1101
splitDicFileWord(const OUString & rDicFileWord,OUString & rDicWord,OUString & rReplacement)1102 void DicEntry::splitDicFileWord(const OUString &rDicFileWord,
1103 OUString &rDicWord,
1104 OUString &rReplacement)
1105 {
1106 MutexGuard aGuard( GetLinguMutex() );
1107
1108 static const OUString aDelim( A2OU( "==" ) );
1109
1110 sal_Int32 nDelimPos = rDicFileWord.indexOf( aDelim );
1111 if (-1 != nDelimPos)
1112 {
1113 sal_Int32 nTriplePos = nDelimPos + 2;
1114 if ( nTriplePos < rDicFileWord.getLength()
1115 && rDicFileWord[ nTriplePos ] == '=' )
1116 ++nDelimPos;
1117 rDicWord = rDicFileWord.copy( 0, nDelimPos );
1118 rReplacement = rDicFileWord.copy( nDelimPos + 2 );
1119 }
1120 else
1121 {
1122 rDicWord = rDicFileWord;
1123 rReplacement = OUString();
1124 }
1125 }
1126
getDictionaryWord()1127 OUString SAL_CALL DicEntry::getDictionaryWord( )
1128 throw(RuntimeException)
1129 {
1130 MutexGuard aGuard( GetLinguMutex() );
1131 return aDicWord;
1132 }
1133
isNegative()1134 sal_Bool SAL_CALL DicEntry::isNegative( )
1135 throw(RuntimeException)
1136 {
1137 MutexGuard aGuard( GetLinguMutex() );
1138 return bIsNegativ;
1139 }
1140
getReplacementText()1141 OUString SAL_CALL DicEntry::getReplacementText( )
1142 throw(RuntimeException)
1143 {
1144 MutexGuard aGuard( GetLinguMutex() );
1145 return aReplacement;
1146 }
1147
1148
1149 ///////////////////////////////////////////////////////////////////////////
1150
1151