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