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