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