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