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