xref: /AOO41X/main/linguistic/source/dicimp.cxx (revision 79aad27f7f29270c03e208e3d687e8e3850af11d)
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