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