xref: /trunk/main/linguistic/source/dicimp.cxx (revision 91144cd0085a7583d2099b982122deb2184ab956)
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 initially (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 {
706     uno::Reference< XInterface > xService =
707             (cppu::OWeakObject*) new DictionaryNeo;
708     return xService;
709 }
710 
getName()711 OUString SAL_CALL DictionaryNeo::getName(  )
712 {
713     MutexGuard  aGuard( GetLinguMutex() );
714     return aDicName;
715 }
716 
setName(const OUString & aName)717 void SAL_CALL DictionaryNeo::setName( const OUString& aName )
718 {
719     MutexGuard  aGuard( GetLinguMutex() );
720 
721     if (aDicName != aName)
722     {
723         aDicName = aName;
724         launchEvent(DictionaryEventFlags::CHG_NAME, NULL);
725     }
726 }
727 
getDictionaryType()728 DictionaryType SAL_CALL DictionaryNeo::getDictionaryType(  )
729 {
730     MutexGuard  aGuard( GetLinguMutex() );
731 
732     return eDicType;
733 }
734 
setActive(sal_Bool bActivate)735 void SAL_CALL DictionaryNeo::setActive( sal_Bool bActivate )
736 {
737     MutexGuard  aGuard( GetLinguMutex() );
738 
739     if (bIsActive != bActivate)
740     {
741         bIsActive = bActivate != 0;
742         sal_Int16 nEvent = bIsActive ?
743                 DictionaryEventFlags::ACTIVATE_DIC : DictionaryEventFlags::DEACTIVATE_DIC;
744 
745         // remove entries from memory if dictionary is deactivated
746         if (bIsActive == sal_False)
747         {
748             sal_Bool bIsEmpty = nCount == 0;
749 
750             // save entries first if necessary
751             if (bIsModified && hasLocation() && !isReadonly())
752             {
753                 store();
754 
755                 aEntries.realloc( 0 );
756                 nCount = 0;
757                 bNeedEntries = !bIsEmpty;
758             }
759             DBG_ASSERT( !bIsModified || !hasLocation() || isReadonly(),
760                     "lng : dictionary is still modified" );
761         }
762 
763         launchEvent(nEvent, NULL);
764     }
765 }
766 
isActive()767 sal_Bool SAL_CALL DictionaryNeo::isActive(  )
768 {
769     MutexGuard  aGuard( GetLinguMutex() );
770     return bIsActive;
771 }
772 
getCount()773 sal_Int32 SAL_CALL DictionaryNeo::getCount(  )
774 {
775     MutexGuard  aGuard( GetLinguMutex() );
776 
777     if (bNeedEntries)
778         loadEntries( aMainURL );
779     return nCount;
780 }
781 
getLocale()782 Locale SAL_CALL DictionaryNeo::getLocale(  )
783 {
784     MutexGuard  aGuard( GetLinguMutex() );
785     Locale aRes;
786     return LanguageToLocale( aRes, nLanguage );
787 }
788 
setLocale(const Locale & aLocale)789 void SAL_CALL DictionaryNeo::setLocale( const Locale& aLocale )
790 {
791     MutexGuard  aGuard( GetLinguMutex() );
792     sal_Int16 nLanguageP = LocaleToLanguage( aLocale );
793     if (!bIsReadonly  &&  nLanguage != nLanguageP)
794     {
795         nLanguage = nLanguageP;
796         bIsModified = sal_True; // new language needs to be saved with dictionary
797 
798         launchEvent( DictionaryEventFlags::CHG_LANGUAGE, NULL );
799     }
800 }
801 
getEntry(const OUString & aWord)802 uno::Reference< XDictionaryEntry > SAL_CALL DictionaryNeo::getEntry(
803             const OUString& aWord )
804 {
805     MutexGuard  aGuard( GetLinguMutex() );
806 
807     if (bNeedEntries)
808         loadEntries( aMainURL );
809 
810     sal_Int32 nPos;
811     sal_Bool bFound = seekEntry( aWord, &nPos, sal_True );
812     DBG_ASSERT( nCount <= aEntries.getLength(), "lng : wrong number of entries");
813     DBG_ASSERT(!bFound || nPos < nCount, "lng : index out of range");
814 
815     return bFound ? aEntries.getConstArray()[ nPos ]
816                     : uno::Reference< XDictionaryEntry >();
817 }
818 
addEntry(const uno::Reference<XDictionaryEntry> & xDicEntry)819 sal_Bool SAL_CALL DictionaryNeo::addEntry(
820             const uno::Reference< XDictionaryEntry >& xDicEntry )
821 {
822     MutexGuard  aGuard( GetLinguMutex() );
823 
824     sal_Bool bRes = sal_False;
825 
826     if (!bIsReadonly)
827     {
828         if (bNeedEntries)
829             loadEntries( aMainURL );
830         bRes = addEntry_Impl( xDicEntry );
831     }
832 
833     return bRes;
834 }
835 
836 sal_Bool SAL_CALL
add(const OUString & rWord,sal_Bool bIsNegative,const OUString & rRplcText)837     DictionaryNeo::add( const OUString& rWord, sal_Bool bIsNegative,
838             const OUString& rRplcText )
839 {
840     MutexGuard  aGuard( GetLinguMutex() );
841 
842     sal_Bool bRes = sal_False;
843 
844     if (!bIsReadonly)
845     {
846         uno::Reference< XDictionaryEntry > xEntry =
847                 new DicEntry( rWord, bIsNegative, rRplcText );
848         bRes = addEntry_Impl( xEntry );
849     }
850 
851     return bRes;
852 }
853 
lcl_SequenceRemoveElementAt(uno::Sequence<uno::Reference<XDictionaryEntry>> & rEntries,int nPos)854 void lcl_SequenceRemoveElementAt(
855             uno::Sequence< uno::Reference< XDictionaryEntry > >& rEntries, int nPos )
856 {
857     //TODO: helper for SequenceRemoveElementAt available?
858     if(nPos >= rEntries.getLength())
859         return;
860     uno::Sequence< uno::Reference< XDictionaryEntry > > aTmp(rEntries.getLength() - 1);
861     uno::Reference< XDictionaryEntry > * pOrig = rEntries.getArray();
862     uno::Reference< XDictionaryEntry > * pTemp = aTmp.getArray();
863     int nOffset = 0;
864     for(int i = 0; i < aTmp.getLength(); i++)
865     {
866         if(nPos == i)
867             nOffset++;
868         pTemp[i] = pOrig[i + nOffset];
869     }
870 
871     rEntries = aTmp;
872 }
873 
remove(const OUString & aWord)874 sal_Bool SAL_CALL DictionaryNeo::remove( const OUString& aWord )
875 {
876     MutexGuard  aGuard( GetLinguMutex() );
877 
878     sal_Bool bRemoved = sal_False;
879 
880     if (!bIsReadonly)
881     {
882         if (bNeedEntries)
883             loadEntries( aMainURL );
884 
885         sal_Int32 nPos;
886         sal_Bool bFound = seekEntry( aWord, &nPos );
887         DBG_ASSERT( nCount < aEntries.getLength(),
888                 "lng : wrong number of entries");
889         DBG_ASSERT(!bFound || nPos < nCount, "lng : index out of range");
890 
891         // remove element if found
892         if (bFound)
893         {
894             // entry to be removed
895             uno::Reference< XDictionaryEntry >
896                     xDicEntry( aEntries.getConstArray()[ nPos ] );
897             DBG_ASSERT(xDicEntry.is(), "lng : dictionary entry is NULL");
898 
899             nCount--;
900 
901             //! the following call reduces the length of the sequence by 1 also
902             lcl_SequenceRemoveElementAt( aEntries, nPos );
903             bRemoved = bIsModified = sal_True;
904 
905             launchEvent( DictionaryEventFlags::DEL_ENTRY, xDicEntry );
906         }
907     }
908 
909     return bRemoved;
910 }
911 
isFull()912 sal_Bool SAL_CALL DictionaryNeo::isFull(  )
913 {
914     MutexGuard  aGuard( GetLinguMutex() );
915 
916     if (bNeedEntries)
917         loadEntries( aMainURL );
918     return nCount >= DIC_MAX_ENTRIES;
919 }
920 
921 uno::Sequence< uno::Reference< XDictionaryEntry > >
getEntries()922     SAL_CALL DictionaryNeo::getEntries(  )
923 {
924     MutexGuard  aGuard( GetLinguMutex() );
925 
926     if (bNeedEntries)
927         loadEntries( aMainURL );
928     //! return sequence with length equal to the number of dictionary entries
929     //! (internal used sequence may have additional unused elements.)
930     return uno::Sequence< uno::Reference< XDictionaryEntry > >
931         (aEntries.getConstArray(), nCount);
932 }
933 
934 
clear()935 void SAL_CALL DictionaryNeo::clear(  )
936 {
937     MutexGuard  aGuard( GetLinguMutex() );
938 
939     if (!bIsReadonly && nCount)
940     {
941         // release all references to old entries and provide space for new ones
942         aEntries = uno::Sequence< uno::Reference< XDictionaryEntry > > ( 32 );
943 
944         nCount = 0;
945         bNeedEntries = sal_False;
946         bIsModified = sal_True;
947 
948         launchEvent( DictionaryEventFlags::ENTRIES_CLEARED , NULL );
949     }
950 }
951 
addDictionaryEventListener(const uno::Reference<XDictionaryEventListener> & xListener)952 sal_Bool SAL_CALL DictionaryNeo::addDictionaryEventListener(
953             const uno::Reference< XDictionaryEventListener >& xListener )
954 {
955     MutexGuard  aGuard( GetLinguMutex() );
956 
957     sal_Bool bRes = sal_False;
958     if (xListener.is())
959     {
960         sal_Int32   nLen = aDicEvtListeners.getLength();
961         bRes = aDicEvtListeners.addInterface( xListener ) != nLen;
962     }
963     return bRes;
964 }
965 
removeDictionaryEventListener(const uno::Reference<XDictionaryEventListener> & xListener)966 sal_Bool SAL_CALL DictionaryNeo::removeDictionaryEventListener(
967             const uno::Reference< XDictionaryEventListener >& xListener )
968 {
969     MutexGuard  aGuard( GetLinguMutex() );
970 
971     sal_Bool bRes = sal_False;
972     if (xListener.is())
973     {
974         sal_Int32   nLen = aDicEvtListeners.getLength();
975         bRes = aDicEvtListeners.removeInterface( xListener ) != nLen;
976     }
977     return bRes;
978 }
979 
980 
hasLocation()981 sal_Bool SAL_CALL DictionaryNeo::hasLocation()
982 {
983     MutexGuard  aGuard( GetLinguMutex() );
984     return aMainURL.getLength() > 0;
985 }
986 
getLocation()987 OUString SAL_CALL DictionaryNeo::getLocation()
988 {
989     MutexGuard  aGuard( GetLinguMutex() );
990     return aMainURL;
991 }
992 
isReadonly()993 sal_Bool SAL_CALL DictionaryNeo::isReadonly()
994 {
995     MutexGuard  aGuard( GetLinguMutex() );
996 
997     return bIsReadonly;
998 }
999 
store()1000 void SAL_CALL DictionaryNeo::store()
1001 {
1002     MutexGuard  aGuard( GetLinguMutex() );
1003 
1004     if (bIsModified && hasLocation() && !isReadonly())
1005     {
1006         if (saveEntries( aMainURL ))
1007         {
1008 #ifdef LINGU_EXCEPTIONS
1009             throw io::IOException();
1010 #endif
1011         }
1012         else
1013             bIsModified = sal_False;
1014     }
1015 }
1016 
storeAsURL(const OUString & aURL,const uno::Sequence<beans::PropertyValue> &)1017 void SAL_CALL DictionaryNeo::storeAsURL(
1018             const OUString& aURL,
1019             const uno::Sequence< beans::PropertyValue >& /*rArgs*/ )
1020 {
1021     MutexGuard  aGuard( GetLinguMutex() );
1022 
1023     if (saveEntries( aURL ))
1024     {
1025 #ifdef LINGU_EXCEPTIONS
1026         throw io::IOException();
1027 #endif
1028     }
1029     else
1030     {
1031         aMainURL = aURL;
1032         bIsModified = sal_False;
1033         bIsReadonly = IsReadOnly( getLocation() );
1034     }
1035 }
1036 
storeToURL(const OUString & aURL,const uno::Sequence<beans::PropertyValue> &)1037 void SAL_CALL DictionaryNeo::storeToURL(
1038             const OUString& aURL,
1039             const uno::Sequence< beans::PropertyValue >& /*rArgs*/ )
1040 {
1041     MutexGuard  aGuard( GetLinguMutex() );
1042 
1043     if (saveEntries( aURL ))
1044     {
1045 #ifdef LINGU_EXCEPTIONS
1046         throw io::IOException();
1047 #endif
1048     }
1049 }
1050 
1051 ///////////////////////////////////////////////////////////////////////////
1052 
DicEntry()1053 DicEntry::DicEntry()
1054 {
1055     bIsNegativ = sal_False;
1056 }
1057 
DicEntry(const OUString & rDicFileWord,sal_Bool bIsNegativWord)1058 DicEntry::DicEntry(const OUString &rDicFileWord,
1059                    sal_Bool bIsNegativWord)
1060 {
1061     if (rDicFileWord.getLength())
1062         splitDicFileWord( rDicFileWord, aDicWord, aReplacement );
1063     bIsNegativ = bIsNegativWord;
1064 }
1065 
DicEntry(const OUString & rDicWord,sal_Bool bNegativ,const OUString & rRplcText)1066 DicEntry::DicEntry(const OUString &rDicWord, sal_Bool bNegativ,
1067                    const OUString &rRplcText) :
1068     aDicWord                (rDicWord),
1069     aReplacement            (rRplcText),
1070     bIsNegativ              (bNegativ)
1071 {
1072 }
1073 
~DicEntry()1074 DicEntry::~DicEntry()
1075 {
1076 }
1077 
splitDicFileWord(const OUString & rDicFileWord,OUString & rDicWord,OUString & rReplacement)1078 void DicEntry::splitDicFileWord(const OUString &rDicFileWord,
1079                                 OUString &rDicWord,
1080                                 OUString &rReplacement)
1081 {
1082     MutexGuard  aGuard( GetLinguMutex() );
1083 
1084     static const OUString aDelim( A2OU( "==" ) );
1085 
1086     sal_Int32 nDelimPos = rDicFileWord.indexOf( aDelim );
1087     if (-1 != nDelimPos)
1088     {
1089         sal_Int32 nTriplePos = nDelimPos + 2;
1090         if (    nTriplePos < rDicFileWord.getLength()
1091             &&  rDicFileWord[ nTriplePos ] == '=' )
1092             ++nDelimPos;
1093         rDicWord     = rDicFileWord.copy( 0, nDelimPos );
1094         rReplacement = rDicFileWord.copy( nDelimPos + 2 );
1095     }
1096     else
1097     {
1098         rDicWord     = rDicFileWord;
1099         rReplacement = OUString();
1100     }
1101 }
1102 
getDictionaryWord()1103 OUString SAL_CALL DicEntry::getDictionaryWord(  )
1104 {
1105     MutexGuard  aGuard( GetLinguMutex() );
1106     return aDicWord;
1107 }
1108 
isNegative()1109 sal_Bool SAL_CALL DicEntry::isNegative(  )
1110 {
1111     MutexGuard  aGuard( GetLinguMutex() );
1112     return bIsNegativ;
1113 }
1114 
getReplacementText()1115 OUString SAL_CALL DicEntry::getReplacementText(  )
1116 {
1117     MutexGuard  aGuard( GetLinguMutex() );
1118     return aReplacement;
1119 }
1120 
1121 
1122 ///////////////////////////////////////////////////////////////////////////
1123