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 1506 : static bool getTag(const OString &rLine, const sal_Char *pTagName,
71 : OString &rTagValue)
72 : {
73 1506 : sal_Int32 nPos = rLine.indexOf(pTagName);
74 1506 : if (nPos == -1)
75 1004 : return false;
76 :
77 1004 : rTagValue = comphelper::string::strip(rLine.copy(nPos + rtl_str_getLength(pTagName)),
78 502 : ' ');
79 502 : return true;
80 : }
81 :
82 :
83 251 : sal_Int16 ReadDicVersion( SvStreamPtr &rpStream, sal_uInt16 &nLng, bool &bNeg )
84 : {
85 : // Sniff the header
86 251 : sal_Int16 nDicVersion = DIC_VERSION_DONTKNOW;
87 : sal_Char pMagicHeader[MAX_HEADER_LENGTH];
88 :
89 251 : nLng = LANGUAGE_NONE;
90 251 : bNeg = false;
91 :
92 251 : if (!rpStream.get() || rpStream->GetError())
93 0 : return -1;
94 :
95 251 : sal_Size nSniffPos = rpStream->Tell();
96 251 : static sal_Size nVerOOo7Len = sal::static_int_cast< sal_Size >(strlen( pVerOOo7 ));
97 251 : pMagicHeader[ nVerOOo7Len ] = '\0';
98 502 : if ((rpStream->Read((void *) pMagicHeader, nVerOOo7Len) == nVerOOo7Len) &&
99 251 : !strcmp(pMagicHeader, pVerOOo7))
100 : {
101 : bool bSuccess;
102 251 : OString aLine;
103 :
104 251 : nDicVersion = DIC_VERSION_7;
105 :
106 : // 1st skip magic / header line
107 251 : rpStream->ReadLine(aLine);
108 :
109 : // 2nd line: language all | en-US | pt-BR ...
110 1004 : while ((bSuccess = rpStream->ReadLine(aLine)))
111 : {
112 753 : OString aTagValue;
113 :
114 753 : if (aLine[0] == '#') // skip comments
115 0 : continue;
116 :
117 : // lang: field
118 753 : if (getTag(aLine, "lang: ", aTagValue))
119 : {
120 251 : if (aTagValue == "<none>")
121 85 : nLng = LANGUAGE_NONE;
122 : else
123 : nLng = LanguageTag::convertToLanguageTypeWithFallback(
124 166 : OStringToOUString( aTagValue, RTL_TEXTENCODING_ASCII_US));
125 : }
126 :
127 : // type: negative / positive
128 753 : if (getTag(aLine, "type: ", aTagValue))
129 : {
130 251 : if (aTagValue == "negative")
131 0 : bNeg = true;
132 : else
133 251 : bNeg = false;
134 : }
135 :
136 753 : if (aLine.indexOf("---") != -1) // end of header
137 251 : break;
138 502 : }
139 251 : 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 251 : return nDicVersion;
181 : }
182 :
183 237 : DictionaryNeo::DictionaryNeo(const OUString &rName,
184 : sal_Int16 nLang, DictionaryType eType,
185 : const OUString &rMainURL,
186 : bool bWriteable) :
187 237 : aDicEvtListeners( GetLinguMutex() ),
188 : aDicName (rName),
189 : aMainURL (rMainURL),
190 : eDicType (eType),
191 474 : nLanguage (nLang)
192 : {
193 237 : nCount = 0;
194 237 : nDicVersion = DIC_VERSION_DONTKNOW;
195 237 : bNeedEntries = true;
196 237 : bIsModified = bIsActive = false;
197 237 : bIsReadonly = !bWriteable;
198 :
199 237 : if( !rMainURL.isEmpty())
200 : {
201 157 : bool bExists = FileExists( rMainURL );
202 157 : 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 80 : bIsReadonly = false;
221 80 : bNeedEntries = false;
222 : }
223 237 : }
224 :
225 444 : DictionaryNeo::~DictionaryNeo()
226 : {
227 444 : }
228 :
229 71 : sal_uLong DictionaryNeo::loadEntries(const OUString &rMainURL)
230 : {
231 71 : 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 71 : bNeedEntries = false;
239 :
240 71 : if (rMainURL.isEmpty())
241 0 : return 0;
242 :
243 142 : uno::Reference< uno::XComponentContext > xContext( comphelper::getProcessComponentContext() );
244 :
245 : // get XInputStream stream
246 142 : uno::Reference< io::XInputStream > xStream;
247 : try
248 : {
249 71 : uno::Reference< ucb::XSimpleFileAccess3 > xAccess( ucb::SimpleFileAccess::create(xContext) );
250 71 : xStream = xAccess->openFileRead( rMainURL );
251 : }
252 0 : catch (const uno::Exception &)
253 : {
254 : DBG_ASSERT( false, "failed to get input stream" );
255 : }
256 71 : if (!xStream.is())
257 0 : return static_cast< sal_uLong >(-1);
258 :
259 142 : SvStreamPtr pStream = SvStreamPtr( utl::UcbStreamHelper::CreateStream( xStream ) );
260 :
261 71 : sal_uLong nErr = sal::static_int_cast< sal_uLong >(-1);
262 :
263 : // read header
264 : bool bNegativ;
265 : sal_uInt16 nLang;
266 71 : nDicVersion = ReadDicVersion(pStream, nLang, bNegativ);
267 71 : if (0 != (nErr = pStream->GetError()))
268 0 : return nErr;
269 :
270 71 : nLanguage = nLang;
271 :
272 71 : eDicType = bNegativ ? DictionaryType_NEGATIVE : DictionaryType_POSITIVE;
273 :
274 71 : rtl_TextEncoding eEnc = osl_getThreadTextEncoding();
275 71 : if (nDicVersion >= DIC_VERSION_6)
276 71 : eEnc = RTL_TEXTENCODING_UTF8;
277 71 : nCount = 0;
278 :
279 142 : if (DIC_VERSION_6 == nDicVersion ||
280 142 : DIC_VERSION_5 == nDicVersion ||
281 71 : 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 71 : else if (DIC_VERSION_7 == nDicVersion)
333 : {
334 : bool bSuccess;
335 71 : OString aLine;
336 :
337 : // remaining lines - stock strings (a [==] b)
338 9464 : while (true == (bSuccess = pStream->ReadLine(aLine)))
339 : {
340 9322 : if (aLine[0] == '#') // skip comments
341 0 : continue;
342 9322 : OUString aText = OStringToOUString(aLine, RTL_TEXTENCODING_UTF8);
343 : uno::Reference< XDictionaryEntry > xEntry =
344 18644 : new DicEntry( aText, eDicType == DictionaryType_NEGATIVE );
345 9322 : addEntry_Impl( xEntry, true ); //! don't launch events here
346 9393 : }
347 : }
348 :
349 : SAL_WARN_IF(!isSorted(), "linguistic", "dictionary is not sorted");
350 :
351 : // since this routine should be called only initialy (prior to any
352 : // modification to be saved) we reset the bIsModified flag here that
353 : // was implicitly set by addEntry_Impl
354 71 : bIsModified = false;
355 :
356 142 : return pStream->GetError();
357 : }
358 :
359 0 : static OString formatForSave(const uno::Reference< XDictionaryEntry > &xEntry,
360 : rtl_TextEncoding eEnc )
361 : {
362 0 : OStringBuffer aStr(OUStringToOString(xEntry->getDictionaryWord(), eEnc));
363 :
364 0 : if (xEntry->isNegative())
365 : {
366 0 : aStr.append("==");
367 0 : aStr.append(OUStringToOString(xEntry->getReplacementText(), eEnc));
368 : }
369 0 : return aStr.makeStringAndClear();
370 : }
371 :
372 : struct TmpDictionary
373 : {
374 : OUString maURL, maTmpURL;
375 : uno::Reference< ucb::XSimpleFileAccess3 > mxAccess;
376 :
377 1 : void cleanTmpFile()
378 : {
379 : try
380 : {
381 1 : if (mxAccess.is())
382 : {
383 1 : mxAccess->kill(maTmpURL);
384 : }
385 : }
386 1 : catch (const uno::Exception &) { }
387 1 : }
388 1 : TmpDictionary(const OUString &rURL)
389 1 : : maURL( rURL )
390 : {
391 1 : maTmpURL = maURL + ".tmp";
392 1 : }
393 1 : ~TmpDictionary()
394 1 : {
395 1 : cleanTmpFile();
396 1 : }
397 :
398 1 : uno::Reference< io::XStream > openTmpFile()
399 : {
400 1 : uno::Reference< io::XStream > xStream;
401 :
402 : try
403 : {
404 2 : mxAccess = ucb::SimpleFileAccess::create(
405 1 : comphelper::getProcessComponentContext());
406 1 : xStream = mxAccess->openFileReadWrite(maTmpURL);
407 0 : } catch (const uno::Exception &) { }
408 :
409 1 : return xStream;
410 : }
411 :
412 1 : sal_uLong renameTmpToURL()
413 : {
414 : try
415 : {
416 1 : mxAccess->move(maTmpURL, maURL);
417 : }
418 0 : catch (const uno::Exception &)
419 : {
420 : DBG_ASSERT( false, "failed to overwrite dict" );
421 0 : return static_cast< sal_uLong >(-1);
422 : }
423 1 : return 0;
424 : }
425 : };
426 :
427 1 : sal_uLong DictionaryNeo::saveEntries(const OUString &rURL)
428 : {
429 1 : MutexGuard aGuard( GetLinguMutex() );
430 :
431 1 : if (rURL.isEmpty())
432 0 : return 0;
433 : DBG_ASSERT(!INetURLObject( rURL ).HasError(), "lng : invalid URL");
434 :
435 : // lifecycle manage the .tmp file
436 2 : TmpDictionary aTmpDictionary(rURL);
437 2 : uno::Reference< io::XStream > xStream = aTmpDictionary.openTmpFile();
438 :
439 1 : if (!xStream.is())
440 0 : return static_cast< sal_uLong >(-1);
441 :
442 1 : sal_uLong nErr = sal::static_int_cast< sal_uLong >(-1);
443 2 : SvStreamPtr pStream = SvStreamPtr( utl::UcbStreamHelper::CreateStream( xStream ) );
444 :
445 :
446 : // Always write as the latest version, i.e. DIC_VERSION_7
447 :
448 1 : rtl_TextEncoding eEnc = RTL_TEXTENCODING_UTF8;
449 1 : pStream->WriteLine(OString(pVerOOo7));
450 1 : if (0 != (nErr = pStream->GetError()))
451 0 : return nErr;
452 : /* XXX: the <none> case could be differentiated, is it absence or
453 : * undetermined or multiple? Earlier versions did not know about 'und' and
454 : * 'mul' and 'zxx' codes. Sync with ReadDicVersion() */
455 1 : if (LinguIsUnspecified(nLanguage))
456 1 : pStream->WriteLine(OString("lang: <none>"));
457 : else
458 : {
459 0 : OStringBuffer aLine("lang: ");
460 0 : aLine.append(OUStringToOString(LanguageTag::convertToBcp47(nLanguage), eEnc));
461 0 : pStream->WriteLine(aLine.makeStringAndClear());
462 : }
463 1 : if (0 != (nErr = pStream->GetError()))
464 0 : return nErr;
465 1 : if (eDicType == DictionaryType_POSITIVE)
466 1 : pStream->WriteLine(OString("type: positive"));
467 : else
468 0 : pStream->WriteLine(OString("type: negative"));
469 1 : if (0 != (nErr = pStream->GetError()))
470 0 : return nErr;
471 1 : pStream->WriteLine(OString("---"));
472 1 : if (0 != (nErr = pStream->GetError()))
473 0 : return nErr;
474 1 : const uno::Reference< XDictionaryEntry > *pEntry = aEntries.getConstArray();
475 1 : for (sal_Int32 i = 0; i < nCount; i++)
476 : {
477 0 : OString aOutStr = formatForSave(pEntry[i], eEnc);
478 0 : pStream->WriteLine (aOutStr);
479 0 : if (0 != (nErr = pStream->GetError()))
480 0 : break;
481 0 : }
482 :
483 1 : pStream.reset(); // fdo#66420 close streams so Win32 can move the file
484 1 : xStream.clear();
485 1 : nErr = aTmpDictionary.renameTmpToURL();
486 :
487 : //If we are migrating from an older version, then on first successful
488 : //write, we're now converted to the latest version, i.e. DIC_VERSION_7
489 1 : nDicVersion = DIC_VERSION_7;
490 :
491 2 : return nErr;
492 : }
493 :
494 223 : void DictionaryNeo::launchEvent(sal_Int16 nEvent,
495 : uno::Reference< XDictionaryEntry > xEntry)
496 : {
497 223 : MutexGuard aGuard( GetLinguMutex() );
498 :
499 446 : DictionaryEvent aEvt;
500 223 : aEvt.Source = uno::Reference< XDictionary >( this );
501 223 : aEvt.nEvent = nEvent;
502 223 : aEvt.xDictionaryEntry = xEntry;
503 :
504 446 : cppu::OInterfaceIteratorHelper aIt( aDicEvtListeners );
505 625 : while (aIt.hasMoreElements())
506 : {
507 179 : uno::Reference< XDictionaryEventListener > xRef( aIt.next(), UNO_QUERY );
508 179 : if (xRef.is())
509 179 : xRef->processDictionaryEvent( aEvt );
510 402 : }
511 223 : }
512 :
513 562006 : int DictionaryNeo::cmpDicEntry(const OUString& rWord1,
514 : const OUString &rWord2,
515 : bool bSimilarOnly)
516 : {
517 562006 : MutexGuard aGuard( GetLinguMutex() );
518 :
519 : // returns 0 if rWord1 is equal to rWord2
520 : // " a value < 0 if rWord1 is less than rWord2
521 : // " a value > 0 if rWord1 is greater than rWord2
522 :
523 562006 : int nRes = 0;
524 :
525 1124012 : OUString aWord1( rWord1 ),
526 1124012 : aWord2( rWord2 );
527 562006 : sal_Int32 nLen1 = aWord1.getLength(),
528 562006 : nLen2 = aWord2.getLength();
529 562006 : if (bSimilarOnly)
530 : {
531 503816 : const sal_Unicode cChar = '.';
532 503816 : if (nLen1 && cChar == aWord1[ nLen1 - 1 ])
533 0 : nLen1--;
534 503816 : if (nLen2 && cChar == aWord2[ nLen2 - 1 ])
535 616 : nLen2--;
536 : }
537 :
538 562006 : const sal_Unicode cIgnChar = '=';
539 562006 : const sal_Unicode cIgnBeg = '['; // for alternative hyphenation, eg. Schif[f]fahrt, Zuc[1k]ker
540 562006 : const sal_Unicode cIgnEnd = ']'; // planned: gee"[1-/e]rfde or ge[-/1e]e"rfde (gee"rfde -> ge=erfde)
541 562006 : sal_Int32 nIdx1 = 0,
542 562006 : nIdx2 = 0,
543 562006 : nNumIgnChar1 = 0,
544 562006 : nNumIgnChar2 = 0;
545 :
546 : bool IgnState;
547 562006 : sal_Int32 nDiff = 0;
548 562006 : sal_Unicode cChar1 = '\0';
549 562006 : sal_Unicode cChar2 = '\0';
550 193316 : do
551 : {
552 : // skip chars to be ignored
553 754067 : IgnState = false;
554 1508206 : while (nIdx1 < nLen1 && ((cChar1 = aWord1[ nIdx1 ]) == cIgnChar || cChar1 == cIgnBeg || IgnState ))
555 : {
556 72 : if ( cChar1 == cIgnBeg )
557 0 : IgnState = true;
558 72 : else if (cChar1 == cIgnEnd)
559 0 : IgnState = false;
560 72 : nIdx1++;
561 72 : nNumIgnChar1++;
562 : }
563 754067 : IgnState = false;
564 1508134 : while (nIdx2 < nLen2 && ((cChar2 = aWord2[ nIdx2 ]) == cIgnChar || cChar2 == cIgnBeg || IgnState ))
565 : {
566 0 : if ( cChar2 == cIgnBeg )
567 0 : IgnState = true;
568 0 : else if (cChar2 == cIgnEnd)
569 0 : IgnState = false;
570 0 : nIdx2++;
571 0 : nNumIgnChar2++;
572 : }
573 :
574 754067 : if (nIdx1 < nLen1 && nIdx2 < nLen2)
575 : {
576 753995 : nDiff = cChar1 - cChar2;
577 753995 : if (nDiff)
578 560751 : break;
579 193244 : nIdx1++;
580 193244 : nIdx2++;
581 : }
582 192794 : } while (nIdx1 < nLen1 && nIdx2 < nLen2);
583 :
584 :
585 562006 : if (nDiff)
586 560751 : nRes = nDiff;
587 : else
588 : { // the string with the smallest count of not ignored chars is the
589 : // shorter one
590 :
591 : // count remaining IgnChars
592 1255 : IgnState = false;
593 6785 : while (nIdx1 < nLen1 )
594 : {
595 4275 : if (aWord1[ nIdx1 ] == cIgnBeg)
596 0 : IgnState = true;
597 4275 : if (IgnState || aWord1[ nIdx1 ] == cIgnChar)
598 117 : nNumIgnChar1++;
599 4275 : if (aWord1[ nIdx1] == cIgnEnd)
600 0 : IgnState = false;
601 4275 : nIdx1++;
602 : }
603 1255 : IgnState = false;
604 4108 : while (nIdx2 < nLen2 )
605 : {
606 1598 : if (aWord2[ nIdx2 ] == cIgnBeg)
607 0 : IgnState = true;
608 1598 : if (IgnState || aWord2[ nIdx2 ] == cIgnChar)
609 204 : nNumIgnChar2++;
610 1598 : if (aWord2[ nIdx2 ] == cIgnEnd)
611 0 : IgnState = false;
612 1598 : nIdx2++;
613 : }
614 :
615 1255 : nRes = ((sal_Int32) nLen1 - nNumIgnChar1) - ((sal_Int32) nLen2 - nNumIgnChar2);
616 : }
617 :
618 1124012 : return nRes;
619 : }
620 :
621 140460 : bool DictionaryNeo::seekEntry(const OUString &rWord,
622 : sal_Int32 *pPos, bool bSimilarOnly)
623 : {
624 : // look for entry with binary search.
625 : // return sal_True if found sal_False else.
626 : // if pPos != NULL it will become the position of the found entry, or
627 : // if that was not found the position where it has to be inserted
628 : // to keep the entries sorted
629 :
630 140460 : MutexGuard aGuard( GetLinguMutex() );
631 :
632 140460 : const uno::Reference< XDictionaryEntry > *pEntry = aEntries.getConstArray();
633 140460 : sal_Int32 nUpperIdx = getCount(),
634 : nMidIdx,
635 140460 : nLowerIdx = 0;
636 140460 : if( nUpperIdx > 0 )
637 : {
638 85610 : nUpperIdx--;
639 723125 : while( nLowerIdx <= nUpperIdx )
640 : {
641 562006 : nMidIdx = (nLowerIdx + nUpperIdx) / 2;
642 : DBG_ASSERT(pEntry[nMidIdx].is(), "lng : empty entry encountered");
643 :
644 562006 : int nCmp = - cmpDicEntry( pEntry[nMidIdx]->getDictionaryWord(),
645 1124012 : rWord, bSimilarOnly );
646 562006 : if(nCmp == 0)
647 : {
648 12 : if( pPos ) *pPos = nMidIdx;
649 12 : return true;
650 : }
651 561994 : else if(nCmp > 0)
652 318841 : nLowerIdx = nMidIdx + 1;
653 243153 : else if( nMidIdx == 0 )
654 : {
655 10089 : if( pPos ) *pPos = nLowerIdx;
656 10089 : return false;
657 : }
658 : else
659 233064 : nUpperIdx = nMidIdx - 1;
660 : }
661 : }
662 130359 : if( pPos ) *pPos = nLowerIdx;
663 130359 : return false;
664 : }
665 :
666 0 : bool DictionaryNeo::isSorted()
667 : {
668 0 : bool bRes = true;
669 :
670 0 : const uno::Reference< XDictionaryEntry > *pEntry = aEntries.getConstArray();
671 0 : sal_Int32 nEntries = getCount();
672 : sal_Int32 i;
673 0 : for (i = 1; i < nEntries; i++)
674 : {
675 0 : if (cmpDicEntry( pEntry[i-1]->getDictionaryWord(),
676 0 : pEntry[i]->getDictionaryWord() ) > 0)
677 : {
678 0 : bRes = false;
679 0 : break;
680 : }
681 : }
682 0 : return bRes;
683 : }
684 :
685 9334 : bool DictionaryNeo::addEntry_Impl(const uno::Reference< XDictionaryEntry > xDicEntry,
686 : bool bIsLoadEntries)
687 : {
688 9334 : MutexGuard aGuard( GetLinguMutex() );
689 :
690 9334 : bool bRes = false;
691 :
692 9334 : if ( bIsLoadEntries || (!bIsReadonly && xDicEntry.is()) )
693 : {
694 9334 : bool bIsNegEntry = xDicEntry->isNegative();
695 28002 : bool bAddEntry = !isFull() &&
696 18666 : ( ( eDicType == DictionaryType_POSITIVE && !bIsNegEntry )
697 2 : || ( eDicType == DictionaryType_NEGATIVE && bIsNegEntry )
698 9334 : || ( eDicType == DictionaryType_MIXED ) );
699 :
700 : // look for position to insert entry at
701 : // if there is already an entry do not insert the new one
702 9334 : sal_Int32 nPos = 0;
703 9334 : bool bFound = false;
704 9334 : if (bAddEntry)
705 : {
706 9334 : bFound = seekEntry( xDicEntry->getDictionaryWord(), &nPos );
707 9334 : if (bFound)
708 0 : bAddEntry = false;
709 : }
710 :
711 9334 : if (bAddEntry)
712 : {
713 : DBG_ASSERT(!bNeedEntries, "lng : entries still not loaded");
714 :
715 9334 : if (nCount >= aEntries.getLength())
716 216 : aEntries.realloc( std::max(2 * nCount, nCount + 32) );
717 9334 : uno::Reference< XDictionaryEntry > *pEntry = aEntries.getArray();
718 :
719 : // shift old entries right
720 : sal_Int32 i;
721 87200 : for (i = nCount - 1; i >= nPos; i--)
722 77866 : pEntry[ i+1 ] = pEntry[ i ];
723 : // insert new entry at specified position
724 9334 : pEntry[ nPos ] = xDicEntry;
725 : SAL_WARN_IF(!isSorted(), "linguistic", "dictionary entries unsorted");
726 :
727 9334 : nCount++;
728 :
729 9334 : bIsModified = true;
730 9334 : bRes = true;
731 :
732 9334 : if (!bIsLoadEntries)
733 12 : launchEvent( DictionaryEventFlags::ADD_ENTRY, xDicEntry );
734 : }
735 : }
736 :
737 9334 : return bRes;
738 : }
739 :
740 84053 : OUString SAL_CALL DictionaryNeo::getName( )
741 : throw(RuntimeException, std::exception)
742 : {
743 84053 : MutexGuard aGuard( GetLinguMutex() );
744 84053 : return aDicName;
745 : }
746 :
747 0 : void SAL_CALL DictionaryNeo::setName( const OUString& aName )
748 : throw(RuntimeException, std::exception)
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 274017 : DictionaryType SAL_CALL DictionaryNeo::getDictionaryType( )
760 : throw(RuntimeException, std::exception)
761 : {
762 274017 : MutexGuard aGuard( GetLinguMutex() );
763 :
764 274017 : return eDicType;
765 : }
766 :
767 253 : void SAL_CALL DictionaryNeo::setActive( sal_Bool bActivate )
768 : throw(RuntimeException, std::exception)
769 : {
770 253 : MutexGuard aGuard( GetLinguMutex() );
771 :
772 253 : if ((bIsActive ? 1 : 0) != bActivate)
773 : {
774 211 : bIsActive = bActivate != 0;
775 : sal_Int16 nEvent = bIsActive ?
776 211 : DictionaryEventFlags::ACTIVATE_DIC : DictionaryEventFlags::DEACTIVATE_DIC;
777 :
778 : // remove entries from memory if dictionary is deactivated
779 211 : if (bIsActive == false)
780 : {
781 6 : bool bIsEmpty = nCount == 0;
782 :
783 : // save entries first if necessary
784 6 : 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 211 : launchEvent(nEvent, NULL);
797 253 : }
798 253 : }
799 :
800 273844 : sal_Bool SAL_CALL DictionaryNeo::isActive( )
801 : throw(RuntimeException, std::exception)
802 : {
803 273844 : MutexGuard aGuard( GetLinguMutex() );
804 273844 : return bIsActive;
805 : }
806 :
807 140460 : sal_Int32 SAL_CALL DictionaryNeo::getCount( )
808 : throw(RuntimeException, std::exception)
809 : {
810 140460 : MutexGuard aGuard( GetLinguMutex() );
811 :
812 140460 : if (bNeedEntries)
813 0 : loadEntries( aMainURL );
814 140460 : return nCount;
815 : }
816 :
817 273838 : Locale SAL_CALL DictionaryNeo::getLocale( )
818 : throw(RuntimeException, std::exception)
819 : {
820 273838 : MutexGuard aGuard( GetLinguMutex() );
821 273838 : return LanguageTag::convertToLocale( nLanguage );
822 : }
823 :
824 0 : void SAL_CALL DictionaryNeo::setLocale( const Locale& aLocale )
825 : throw(RuntimeException, std::exception)
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 = true; // new language needs to be saved with dictionary
833 :
834 0 : launchEvent( DictionaryEventFlags::CHG_LANGUAGE, NULL );
835 0 : }
836 0 : }
837 :
838 131126 : uno::Reference< XDictionaryEntry > SAL_CALL DictionaryNeo::getEntry(
839 : const OUString& aWord )
840 : throw(RuntimeException, std::exception)
841 : {
842 131126 : MutexGuard aGuard( GetLinguMutex() );
843 :
844 131126 : if (bNeedEntries)
845 71 : loadEntries( aMainURL );
846 :
847 : sal_Int32 nPos;
848 131126 : bool bFound = seekEntry( aWord, &nPos, 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 24 : return bFound ? aEntries.getConstArray()[ nPos ]
853 131150 : : uno::Reference< XDictionaryEntry >();
854 : }
855 :
856 0 : sal_Bool SAL_CALL DictionaryNeo::addEntry(
857 : const uno::Reference< XDictionaryEntry >& xDicEntry )
858 : throw(RuntimeException, std::exception)
859 : {
860 0 : MutexGuard aGuard( GetLinguMutex() );
861 :
862 0 : bool bRes = 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 12 : DictionaryNeo::add( const OUString& rWord, sal_Bool bIsNegative,
876 : const OUString& rRplcText )
877 : throw(RuntimeException, std::exception)
878 : {
879 12 : MutexGuard aGuard( GetLinguMutex() );
880 :
881 12 : bool bRes = false;
882 :
883 12 : if (!bIsReadonly)
884 : {
885 : uno::Reference< XDictionaryEntry > xEntry =
886 12 : new DicEntry( rWord, bIsNegative, rRplcText );
887 12 : bRes = addEntry_Impl( xEntry );
888 : }
889 :
890 12 : 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, std::exception)
915 : {
916 0 : MutexGuard aGuard( GetLinguMutex() );
917 :
918 0 : bool bRemoved = false;
919 :
920 0 : if (!bIsReadonly)
921 : {
922 0 : if (bNeedEntries)
923 0 : loadEntries( aMainURL );
924 :
925 : sal_Int32 nPos;
926 0 : 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 = true;
944 :
945 0 : launchEvent( DictionaryEventFlags::DEL_ENTRY, xDicEntry );
946 : }
947 : }
948 :
949 0 : return bRemoved;
950 : }
951 :
952 9334 : sal_Bool SAL_CALL DictionaryNeo::isFull( )
953 : throw(RuntimeException, std::exception)
954 : {
955 9334 : MutexGuard aGuard( GetLinguMutex() );
956 :
957 9334 : if (bNeedEntries)
958 0 : loadEntries( aMainURL );
959 9334 : return nCount >= DIC_MAX_ENTRIES;
960 : }
961 :
962 : uno::Sequence< uno::Reference< XDictionaryEntry > >
963 0 : SAL_CALL DictionaryNeo::getEntries( )
964 : throw(RuntimeException, std::exception)
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 130 : void SAL_CALL DictionaryNeo::clear( )
978 : throw(RuntimeException, std::exception)
979 : {
980 130 : MutexGuard aGuard( GetLinguMutex() );
981 :
982 130 : 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 = false;
989 0 : bIsModified = true;
990 :
991 0 : launchEvent( DictionaryEventFlags::ENTRIES_CLEARED , NULL );
992 130 : }
993 130 : }
994 :
995 207 : sal_Bool SAL_CALL DictionaryNeo::addDictionaryEventListener(
996 : const uno::Reference< XDictionaryEventListener >& xListener )
997 : throw(RuntimeException, std::exception)
998 : {
999 207 : MutexGuard aGuard( GetLinguMutex() );
1000 :
1001 207 : bool bRes = false;
1002 207 : if (xListener.is())
1003 : {
1004 207 : sal_Int32 nLen = aDicEvtListeners.getLength();
1005 207 : bRes = aDicEvtListeners.addInterface( xListener ) != nLen;
1006 : }
1007 207 : return bRes;
1008 : }
1009 :
1010 207 : sal_Bool SAL_CALL DictionaryNeo::removeDictionaryEventListener(
1011 : const uno::Reference< XDictionaryEventListener >& xListener )
1012 : throw(RuntimeException, std::exception)
1013 : {
1014 207 : MutexGuard aGuard( GetLinguMutex() );
1015 :
1016 207 : bool bRes = false;
1017 207 : if (xListener.is())
1018 : {
1019 207 : sal_Int32 nLen = aDicEvtListeners.getLength();
1020 207 : bRes = aDicEvtListeners.removeInterface( xListener ) != nLen;
1021 : }
1022 207 : return bRes;
1023 : }
1024 :
1025 :
1026 125 : sal_Bool SAL_CALL DictionaryNeo::hasLocation()
1027 : throw(RuntimeException, std::exception)
1028 : {
1029 125 : MutexGuard aGuard( GetLinguMutex() );
1030 125 : return !aMainURL.isEmpty();
1031 : }
1032 :
1033 0 : OUString SAL_CALL DictionaryNeo::getLocation()
1034 : throw(RuntimeException, std::exception)
1035 : {
1036 0 : MutexGuard aGuard( GetLinguMutex() );
1037 0 : return aMainURL;
1038 : }
1039 :
1040 379 : sal_Bool SAL_CALL DictionaryNeo::isReadonly()
1041 : throw(RuntimeException, std::exception)
1042 : {
1043 379 : MutexGuard aGuard( GetLinguMutex() );
1044 :
1045 379 : return bIsReadonly;
1046 : }
1047 :
1048 39 : void SAL_CALL DictionaryNeo::store()
1049 : throw(io::IOException, RuntimeException, std::exception)
1050 : {
1051 39 : MutexGuard aGuard( GetLinguMutex() );
1052 :
1053 39 : if (bIsModified && hasLocation() && !isReadonly())
1054 : {
1055 0 : if (!saveEntries( aMainURL ))
1056 0 : bIsModified = false;
1057 39 : }
1058 39 : }
1059 :
1060 0 : void SAL_CALL DictionaryNeo::storeAsURL(
1061 : const OUString& aURL,
1062 : const uno::Sequence< beans::PropertyValue >& /*rArgs*/ )
1063 : throw(io::IOException, RuntimeException, std::exception)
1064 : {
1065 0 : MutexGuard aGuard( GetLinguMutex() );
1066 :
1067 0 : if (!saveEntries( aURL ))
1068 : {
1069 0 : aMainURL = aURL;
1070 0 : bIsModified = 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, std::exception)
1079 : {
1080 0 : MutexGuard aGuard( GetLinguMutex() );
1081 0 : saveEntries(aURL);
1082 0 : }
1083 :
1084 :
1085 9322 : DicEntry::DicEntry(const OUString &rDicFileWord,
1086 9322 : bool bIsNegativWord)
1087 : {
1088 9322 : if (!rDicFileWord.isEmpty())
1089 9322 : splitDicFileWord( rDicFileWord, aDicWord, aReplacement );
1090 9322 : bIsNegativ = bIsNegativWord;
1091 9322 : }
1092 :
1093 12 : DicEntry::DicEntry(const OUString &rDicWord, bool bNegativ,
1094 : const OUString &rRplcText) :
1095 : aDicWord (rDicWord),
1096 : aReplacement (rRplcText),
1097 12 : bIsNegativ (bNegativ)
1098 : {
1099 12 : }
1100 :
1101 17018 : DicEntry::~DicEntry()
1102 : {
1103 17018 : }
1104 :
1105 9322 : void DicEntry::splitDicFileWord(const OUString &rDicFileWord,
1106 : OUString &rDicWord,
1107 : OUString &rReplacement)
1108 : {
1109 9322 : MutexGuard aGuard( GetLinguMutex() );
1110 :
1111 9322 : static const OUString aDelim( "==" );
1112 :
1113 9322 : sal_Int32 nDelimPos = rDicFileWord.indexOf( aDelim );
1114 9322 : 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 9322 : rDicWord = rDicFileWord;
1126 9322 : rReplacement = OUString();
1127 9322 : }
1128 9322 : }
1129 :
1130 571340 : OUString SAL_CALL DicEntry::getDictionaryWord( )
1131 : throw(RuntimeException, std::exception)
1132 : {
1133 571340 : MutexGuard aGuard( GetLinguMutex() );
1134 571340 : return aDicWord;
1135 : }
1136 :
1137 9346 : sal_Bool SAL_CALL DicEntry::isNegative( )
1138 : throw(RuntimeException, std::exception)
1139 : {
1140 9346 : MutexGuard aGuard( GetLinguMutex() );
1141 9346 : return bIsNegativ;
1142 : }
1143 :
1144 0 : OUString SAL_CALL DicEntry::getReplacementText( )
1145 : throw(RuntimeException, std::exception)
1146 : {
1147 0 : MutexGuard aGuard( GetLinguMutex() );
1148 0 : return aReplacement;
1149 : }
1150 :
1151 :
1152 :
1153 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|