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 initialy (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 throw(Exception) 706 { 707 uno::Reference< XInterface > xService = 708 (cppu::OWeakObject*) new DictionaryNeo; 709 return xService; 710 } 711 712 OUString SAL_CALL DictionaryNeo::getName( ) 713 throw(RuntimeException) 714 { 715 MutexGuard aGuard( GetLinguMutex() ); 716 return aDicName; 717 } 718 719 void SAL_CALL DictionaryNeo::setName( const OUString& aName ) 720 throw(RuntimeException) 721 { 722 MutexGuard aGuard( GetLinguMutex() ); 723 724 if (aDicName != aName) 725 { 726 aDicName = aName; 727 launchEvent(DictionaryEventFlags::CHG_NAME, NULL); 728 } 729 } 730 731 DictionaryType SAL_CALL DictionaryNeo::getDictionaryType( ) 732 throw(RuntimeException) 733 { 734 MutexGuard aGuard( GetLinguMutex() ); 735 736 return eDicType; 737 } 738 739 void SAL_CALL DictionaryNeo::setActive( sal_Bool bActivate ) 740 throw(RuntimeException) 741 { 742 MutexGuard aGuard( GetLinguMutex() ); 743 744 if (bIsActive != bActivate) 745 { 746 bIsActive = bActivate != 0; 747 sal_Int16 nEvent = bIsActive ? 748 DictionaryEventFlags::ACTIVATE_DIC : DictionaryEventFlags::DEACTIVATE_DIC; 749 750 // remove entries from memory if dictionary is deactivated 751 if (bIsActive == sal_False) 752 { 753 sal_Bool bIsEmpty = nCount == 0; 754 755 // save entries first if necessary 756 if (bIsModified && hasLocation() && !isReadonly()) 757 { 758 store(); 759 760 aEntries.realloc( 0 ); 761 nCount = 0; 762 bNeedEntries = !bIsEmpty; 763 } 764 DBG_ASSERT( !bIsModified || !hasLocation() || isReadonly(), 765 "lng : dictionary is still modified" ); 766 } 767 768 launchEvent(nEvent, NULL); 769 } 770 } 771 772 sal_Bool SAL_CALL DictionaryNeo::isActive( ) 773 throw(RuntimeException) 774 { 775 MutexGuard aGuard( GetLinguMutex() ); 776 return bIsActive; 777 } 778 779 sal_Int32 SAL_CALL DictionaryNeo::getCount( ) 780 throw(RuntimeException) 781 { 782 MutexGuard aGuard( GetLinguMutex() ); 783 784 if (bNeedEntries) 785 loadEntries( aMainURL ); 786 return nCount; 787 } 788 789 Locale SAL_CALL DictionaryNeo::getLocale( ) 790 throw(RuntimeException) 791 { 792 MutexGuard aGuard( GetLinguMutex() ); 793 Locale aRes; 794 return LanguageToLocale( aRes, nLanguage ); 795 } 796 797 void SAL_CALL DictionaryNeo::setLocale( const Locale& aLocale ) 798 throw(RuntimeException) 799 { 800 MutexGuard aGuard( GetLinguMutex() ); 801 sal_Int16 nLanguageP = LocaleToLanguage( aLocale ); 802 if (!bIsReadonly && nLanguage != nLanguageP) 803 { 804 nLanguage = nLanguageP; 805 bIsModified = sal_True; // new language needs to be saved with dictionary 806 807 launchEvent( DictionaryEventFlags::CHG_LANGUAGE, NULL ); 808 } 809 } 810 811 uno::Reference< XDictionaryEntry > SAL_CALL DictionaryNeo::getEntry( 812 const OUString& aWord ) 813 throw(RuntimeException) 814 { 815 MutexGuard aGuard( GetLinguMutex() ); 816 817 if (bNeedEntries) 818 loadEntries( aMainURL ); 819 820 sal_Int32 nPos; 821 sal_Bool bFound = seekEntry( aWord, &nPos, sal_True ); 822 DBG_ASSERT( nCount <= aEntries.getLength(), "lng : wrong number of entries"); 823 DBG_ASSERT(!bFound || nPos < nCount, "lng : index out of range"); 824 825 return bFound ? aEntries.getConstArray()[ nPos ] 826 : uno::Reference< XDictionaryEntry >(); 827 } 828 829 sal_Bool SAL_CALL DictionaryNeo::addEntry( 830 const uno::Reference< XDictionaryEntry >& xDicEntry ) 831 throw(RuntimeException) 832 { 833 MutexGuard aGuard( GetLinguMutex() ); 834 835 sal_Bool bRes = sal_False; 836 837 if (!bIsReadonly) 838 { 839 if (bNeedEntries) 840 loadEntries( aMainURL ); 841 bRes = addEntry_Impl( xDicEntry ); 842 } 843 844 return bRes; 845 } 846 847 sal_Bool SAL_CALL 848 DictionaryNeo::add( const OUString& rWord, sal_Bool bIsNegative, 849 const OUString& rRplcText ) 850 throw(RuntimeException) 851 { 852 MutexGuard aGuard( GetLinguMutex() ); 853 854 sal_Bool bRes = sal_False; 855 856 if (!bIsReadonly) 857 { 858 uno::Reference< XDictionaryEntry > xEntry = 859 new DicEntry( rWord, bIsNegative, rRplcText ); 860 bRes = addEntry_Impl( xEntry ); 861 } 862 863 return bRes; 864 } 865 866 void lcl_SequenceRemoveElementAt( 867 uno::Sequence< uno::Reference< XDictionaryEntry > >& rEntries, int nPos ) 868 { 869 //TODO: helper for SequenceRemoveElementAt available? 870 if(nPos >= rEntries.getLength()) 871 return; 872 uno::Sequence< uno::Reference< XDictionaryEntry > > aTmp(rEntries.getLength() - 1); 873 uno::Reference< XDictionaryEntry > * pOrig = rEntries.getArray(); 874 uno::Reference< XDictionaryEntry > * pTemp = aTmp.getArray(); 875 int nOffset = 0; 876 for(int i = 0; i < aTmp.getLength(); i++) 877 { 878 if(nPos == i) 879 nOffset++; 880 pTemp[i] = pOrig[i + nOffset]; 881 } 882 883 rEntries = aTmp; 884 } 885 886 sal_Bool SAL_CALL DictionaryNeo::remove( const OUString& aWord ) 887 throw(RuntimeException) 888 { 889 MutexGuard aGuard( GetLinguMutex() ); 890 891 sal_Bool bRemoved = sal_False; 892 893 if (!bIsReadonly) 894 { 895 if (bNeedEntries) 896 loadEntries( aMainURL ); 897 898 sal_Int32 nPos; 899 sal_Bool bFound = seekEntry( aWord, &nPos ); 900 DBG_ASSERT( nCount < aEntries.getLength(), 901 "lng : wrong number of entries"); 902 DBG_ASSERT(!bFound || nPos < nCount, "lng : index out of range"); 903 904 // remove element if found 905 if (bFound) 906 { 907 // entry to be removed 908 uno::Reference< XDictionaryEntry > 909 xDicEntry( aEntries.getConstArray()[ nPos ] ); 910 DBG_ASSERT(xDicEntry.is(), "lng : dictionary entry is NULL"); 911 912 nCount--; 913 914 //! the following call reduces the length of the sequence by 1 also 915 lcl_SequenceRemoveElementAt( aEntries, nPos ); 916 bRemoved = bIsModified = sal_True; 917 918 launchEvent( DictionaryEventFlags::DEL_ENTRY, xDicEntry ); 919 } 920 } 921 922 return bRemoved; 923 } 924 925 sal_Bool SAL_CALL DictionaryNeo::isFull( ) 926 throw(RuntimeException) 927 { 928 MutexGuard aGuard( GetLinguMutex() ); 929 930 if (bNeedEntries) 931 loadEntries( aMainURL ); 932 return nCount >= DIC_MAX_ENTRIES; 933 } 934 935 uno::Sequence< uno::Reference< XDictionaryEntry > > 936 SAL_CALL DictionaryNeo::getEntries( ) 937 throw(RuntimeException) 938 { 939 MutexGuard aGuard( GetLinguMutex() ); 940 941 if (bNeedEntries) 942 loadEntries( aMainURL ); 943 //! return sequence with length equal to the number of dictionary entries 944 //! (internal used sequence may have additional unused elements.) 945 return uno::Sequence< uno::Reference< XDictionaryEntry > > 946 (aEntries.getConstArray(), nCount); 947 } 948 949 950 void SAL_CALL DictionaryNeo::clear( ) 951 throw(RuntimeException) 952 { 953 MutexGuard aGuard( GetLinguMutex() ); 954 955 if (!bIsReadonly && nCount) 956 { 957 // release all references to old entries and provide space for new ones 958 aEntries = uno::Sequence< uno::Reference< XDictionaryEntry > > ( 32 ); 959 960 nCount = 0; 961 bNeedEntries = sal_False; 962 bIsModified = sal_True; 963 964 launchEvent( DictionaryEventFlags::ENTRIES_CLEARED , NULL ); 965 } 966 } 967 968 sal_Bool SAL_CALL DictionaryNeo::addDictionaryEventListener( 969 const uno::Reference< XDictionaryEventListener >& xListener ) 970 throw(RuntimeException) 971 { 972 MutexGuard aGuard( GetLinguMutex() ); 973 974 sal_Bool bRes = sal_False; 975 if (xListener.is()) 976 { 977 sal_Int32 nLen = aDicEvtListeners.getLength(); 978 bRes = aDicEvtListeners.addInterface( xListener ) != nLen; 979 } 980 return bRes; 981 } 982 983 sal_Bool SAL_CALL DictionaryNeo::removeDictionaryEventListener( 984 const uno::Reference< XDictionaryEventListener >& xListener ) 985 throw(RuntimeException) 986 { 987 MutexGuard aGuard( GetLinguMutex() ); 988 989 sal_Bool bRes = sal_False; 990 if (xListener.is()) 991 { 992 sal_Int32 nLen = aDicEvtListeners.getLength(); 993 bRes = aDicEvtListeners.removeInterface( xListener ) != nLen; 994 } 995 return bRes; 996 } 997 998 999 sal_Bool SAL_CALL DictionaryNeo::hasLocation() 1000 throw(RuntimeException) 1001 { 1002 MutexGuard aGuard( GetLinguMutex() ); 1003 return aMainURL.getLength() > 0; 1004 } 1005 1006 OUString SAL_CALL DictionaryNeo::getLocation() 1007 throw(RuntimeException) 1008 { 1009 MutexGuard aGuard( GetLinguMutex() ); 1010 return aMainURL; 1011 } 1012 1013 sal_Bool SAL_CALL DictionaryNeo::isReadonly() 1014 throw(RuntimeException) 1015 { 1016 MutexGuard aGuard( GetLinguMutex() ); 1017 1018 return bIsReadonly; 1019 } 1020 1021 void SAL_CALL DictionaryNeo::store() 1022 throw(io::IOException, RuntimeException) 1023 { 1024 MutexGuard aGuard( GetLinguMutex() ); 1025 1026 if (bIsModified && hasLocation() && !isReadonly()) 1027 { 1028 if (saveEntries( aMainURL )) 1029 { 1030 #ifdef LINGU_EXCEPTIONS 1031 throw io::IOException(); 1032 #endif 1033 } 1034 else 1035 bIsModified = sal_False; 1036 } 1037 } 1038 1039 void SAL_CALL DictionaryNeo::storeAsURL( 1040 const OUString& aURL, 1041 const uno::Sequence< beans::PropertyValue >& /*rArgs*/ ) 1042 throw(io::IOException, RuntimeException) 1043 { 1044 MutexGuard aGuard( GetLinguMutex() ); 1045 1046 if (saveEntries( aURL )) 1047 { 1048 #ifdef LINGU_EXCEPTIONS 1049 throw io::IOException(); 1050 #endif 1051 } 1052 else 1053 { 1054 aMainURL = aURL; 1055 bIsModified = sal_False; 1056 bIsReadonly = IsReadOnly( getLocation() ); 1057 } 1058 } 1059 1060 void SAL_CALL DictionaryNeo::storeToURL( 1061 const OUString& aURL, 1062 const uno::Sequence< beans::PropertyValue >& /*rArgs*/ ) 1063 throw(io::IOException, RuntimeException) 1064 { 1065 MutexGuard aGuard( GetLinguMutex() ); 1066 1067 if (saveEntries( aURL )) 1068 { 1069 #ifdef LINGU_EXCEPTIONS 1070 throw io::IOException(); 1071 #endif 1072 } 1073 } 1074 1075 /////////////////////////////////////////////////////////////////////////// 1076 1077 DicEntry::DicEntry() 1078 { 1079 bIsNegativ = sal_False; 1080 } 1081 1082 DicEntry::DicEntry(const OUString &rDicFileWord, 1083 sal_Bool bIsNegativWord) 1084 { 1085 if (rDicFileWord.getLength()) 1086 splitDicFileWord( rDicFileWord, aDicWord, aReplacement ); 1087 bIsNegativ = bIsNegativWord; 1088 } 1089 1090 DicEntry::DicEntry(const OUString &rDicWord, sal_Bool bNegativ, 1091 const OUString &rRplcText) : 1092 aDicWord (rDicWord), 1093 aReplacement (rRplcText), 1094 bIsNegativ (bNegativ) 1095 { 1096 } 1097 1098 DicEntry::~DicEntry() 1099 { 1100 } 1101 1102 void DicEntry::splitDicFileWord(const OUString &rDicFileWord, 1103 OUString &rDicWord, 1104 OUString &rReplacement) 1105 { 1106 MutexGuard aGuard( GetLinguMutex() ); 1107 1108 static const OUString aDelim( A2OU( "==" ) ); 1109 1110 sal_Int32 nDelimPos = rDicFileWord.indexOf( aDelim ); 1111 if (-1 != nDelimPos) 1112 { 1113 sal_Int32 nTriplePos = nDelimPos + 2; 1114 if ( nTriplePos < rDicFileWord.getLength() 1115 && rDicFileWord[ nTriplePos ] == '=' ) 1116 ++nDelimPos; 1117 rDicWord = rDicFileWord.copy( 0, nDelimPos ); 1118 rReplacement = rDicFileWord.copy( nDelimPos + 2 ); 1119 } 1120 else 1121 { 1122 rDicWord = rDicFileWord; 1123 rReplacement = OUString(); 1124 } 1125 } 1126 1127 OUString SAL_CALL DicEntry::getDictionaryWord( ) 1128 throw(RuntimeException) 1129 { 1130 MutexGuard aGuard( GetLinguMutex() ); 1131 return aDicWord; 1132 } 1133 1134 sal_Bool SAL_CALL DicEntry::isNegative( ) 1135 throw(RuntimeException) 1136 { 1137 MutexGuard aGuard( GetLinguMutex() ); 1138 return bIsNegativ; 1139 } 1140 1141 OUString SAL_CALL DicEntry::getReplacementText( ) 1142 throw(RuntimeException) 1143 { 1144 MutexGuard aGuard( GetLinguMutex() ); 1145 return aReplacement; 1146 } 1147 1148 1149 /////////////////////////////////////////////////////////////////////////// 1150 1151