| /************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *************************************************************/ |
| |
| |
| |
| // MARKER(update_precomp.py): autogen include statement, do not remove |
| #include "precompiled_linguistic.hxx" |
| |
| #include <cppuhelper/factory.hxx> |
| #include <dicimp.hxx> |
| #include <hyphdsp.hxx> |
| #include <i18npool/lang.h> |
| #include <i18npool/mslangid.hxx> |
| #include <osl/mutex.hxx> |
| #include <tools/debug.hxx> |
| #include <tools/fsys.hxx> |
| #include <tools/stream.hxx> |
| #include <tools/string.hxx> |
| #include <tools/urlobj.hxx> |
| #include <unotools/processfactory.hxx> |
| #include <unotools/ucbstreamhelper.hxx> |
| |
| #include <com/sun/star/ucb/XSimpleFileAccess.hpp> |
| #include <com/sun/star/linguistic2/DictionaryType.hpp> |
| #include <com/sun/star/linguistic2/DictionaryEventFlags.hpp> |
| #include <com/sun/star/registry/XRegistryKey.hpp> |
| #include <com/sun/star/io/XInputStream.hpp> |
| #include <com/sun/star/io/XOutputStream.hpp> |
| |
| #include "defs.hxx" |
| |
| |
| using namespace utl; |
| using namespace osl; |
| using namespace rtl; |
| using namespace com::sun::star; |
| using namespace com::sun::star::lang; |
| using namespace com::sun::star::uno; |
| using namespace com::sun::star::linguistic2; |
| using namespace linguistic; |
| |
| /////////////////////////////////////////////////////////////////////////// |
| |
| #define BUFSIZE 4096 |
| #define VERS2_NOLANGUAGE 1024 |
| |
| #define MAX_HEADER_LENGTH 16 |
| |
| static const sal_Char* pDicExt = "dic"; |
| static const sal_Char* pVerStr2 = "WBSWG2"; |
| static const sal_Char* pVerStr5 = "WBSWG5"; |
| static const sal_Char* pVerStr6 = "WBSWG6"; |
| static const sal_Char* pVerOOo7 = "OOoUserDict1"; |
| |
| static const sal_Int16 DIC_VERSION_DONTKNOW = -1; |
| static const sal_Int16 DIC_VERSION_2 = 2; |
| static const sal_Int16 DIC_VERSION_5 = 5; |
| static const sal_Int16 DIC_VERSION_6 = 6; |
| static const sal_Int16 DIC_VERSION_7 = 7; |
| |
| static sal_Bool getTag(const ByteString &rLine, |
| const sal_Char *pTagName, ByteString &rTagValue) |
| { |
| xub_StrLen nPos = rLine.Search( pTagName ); |
| if (nPos == STRING_NOTFOUND) |
| return sal_False; |
| |
| rTagValue = rLine.Copy( nPos + sal::static_int_cast< xub_StrLen >(strlen( pTagName )) ).EraseLeadingAndTrailingChars(); |
| return sal_True; |
| } |
| |
| |
| sal_Int16 ReadDicVersion( SvStreamPtr &rpStream, sal_uInt16 &nLng, sal_Bool &bNeg ) |
| { |
| // Sniff the header |
| sal_Int16 nDicVersion = DIC_VERSION_DONTKNOW; |
| sal_Char pMagicHeader[MAX_HEADER_LENGTH]; |
| |
| nLng = LANGUAGE_NONE; |
| bNeg = sal_False; |
| |
| if (!rpStream.get() || rpStream->GetError()) |
| return -1; |
| |
| sal_Size nSniffPos = rpStream->Tell(); |
| static sal_Size nVerOOo7Len = sal::static_int_cast< sal_Size >(strlen( pVerOOo7 )); |
| pMagicHeader[ nVerOOo7Len ] = '\0'; |
| if ((rpStream->Read((void *) pMagicHeader, nVerOOo7Len) == nVerOOo7Len) && |
| !strcmp(pMagicHeader, pVerOOo7)) |
| { |
| sal_Bool bSuccess; |
| ByteString aLine; |
| |
| nDicVersion = DIC_VERSION_7; |
| |
| // 1st skip magic / header line |
| rpStream->ReadLine(aLine); |
| |
| // 2nd line: language all | en-US | pt-BR ... |
| while (sal_True == (bSuccess = rpStream->ReadLine(aLine))) |
| { |
| ByteString aTagValue; |
| |
| if (aLine.GetChar(0) == '#') // skip comments |
| continue; |
| |
| // lang: field |
| if (getTag(aLine, "lang: ", aTagValue)) |
| { |
| if (aTagValue == "<none>") |
| nLng = LANGUAGE_NONE; |
| else |
| nLng = MsLangId::convertIsoStringToLanguage(OUString(aTagValue.GetBuffer(), |
| aTagValue.Len(), RTL_TEXTENCODING_ASCII_US)); |
| } |
| |
| // type: negative / positive |
| if (getTag(aLine, "type: ", aTagValue)) |
| { |
| if (aTagValue == "negative") |
| bNeg = sal_True; |
| else |
| bNeg = sal_False; |
| } |
| |
| if (aLine.Search ("---") != STRING_NOTFOUND) // end of header |
| break; |
| } |
| if (!bSuccess) |
| return -2; |
| } |
| else |
| { |
| sal_uInt16 nLen; |
| |
| rpStream->Seek (nSniffPos ); |
| |
| *rpStream >> nLen; |
| if (nLen >= MAX_HEADER_LENGTH) |
| return -1; |
| |
| rpStream->Read(pMagicHeader, nLen); |
| pMagicHeader[nLen] = '\0'; |
| |
| // Check version magic |
| if (0 == strcmp( pMagicHeader, pVerStr6 )) |
| nDicVersion = DIC_VERSION_6; |
| else if (0 == strcmp( pMagicHeader, pVerStr5 )) |
| nDicVersion = DIC_VERSION_5; |
| else if (0 == strcmp( pMagicHeader, pVerStr2 )) |
| nDicVersion = DIC_VERSION_2; |
| else |
| nDicVersion = DIC_VERSION_DONTKNOW; |
| |
| if (DIC_VERSION_2 == nDicVersion || |
| DIC_VERSION_5 == nDicVersion || |
| DIC_VERSION_6 == nDicVersion) |
| { |
| // The language of the dictionary |
| *rpStream >> nLng; |
| |
| if (VERS2_NOLANGUAGE == nLng) |
| nLng = LANGUAGE_NONE; |
| |
| // Negative Flag |
| sal_Char nTmp; |
| *rpStream >> nTmp; |
| bNeg = (sal_Bool)nTmp; |
| } |
| } |
| |
| return nDicVersion; |
| } |
| |
| |
| |
| const String GetDicExtension() |
| { |
| return String::CreateFromAscii( pDicExt ); |
| } |
| |
| /////////////////////////////////////////////////////////////////////////// |
| |
| DictionaryNeo::DictionaryNeo() : |
| aDicEvtListeners( GetLinguMutex() ), |
| eDicType (DictionaryType_POSITIVE), |
| nLanguage (LANGUAGE_NONE) |
| { |
| nCount = 0; |
| nDicVersion = DIC_VERSION_DONTKNOW; |
| bNeedEntries = sal_False; |
| bIsModified = bIsActive = sal_False; |
| bIsReadonly = sal_False; |
| } |
| |
| DictionaryNeo::DictionaryNeo(const OUString &rName, |
| sal_Int16 nLang, DictionaryType eType, |
| const OUString &rMainURL, |
| sal_Bool bWriteable) : |
| aDicEvtListeners( GetLinguMutex() ), |
| aDicName (rName), |
| aMainURL (rMainURL), |
| eDicType (eType), |
| nLanguage (nLang) |
| { |
| nCount = 0; |
| nDicVersion = DIC_VERSION_DONTKNOW; |
| bNeedEntries = sal_True; |
| bIsModified = bIsActive = sal_False; |
| bIsReadonly = !bWriteable; |
| |
| if( rMainURL.getLength() > 0 ) |
| { |
| sal_Bool bExists = FileExists( rMainURL ); |
| if( !bExists ) |
| { |
| // save new dictionaries with in Format 7 (UTF8 plain text) |
| nDicVersion = DIC_VERSION_7; |
| |
| //! create physical representation of an **empty** dictionary |
| //! that could be found by the dictionary-list implementation |
| // (Note: empty dictionaries are not just empty files!) |
| DBG_ASSERT( !bIsReadonly, |
| "DictionaryNeo: dictionaries should be writeable if they are to be saved" ); |
| if (!bIsReadonly) |
| saveEntries( rMainURL ); |
| bNeedEntries = sal_False; |
| } |
| } |
| else |
| { |
| // non persistent dictionaries (like IgnoreAllList) should always be writable |
| bIsReadonly = sal_False; |
| bNeedEntries = sal_False; |
| } |
| } |
| |
| DictionaryNeo::~DictionaryNeo() |
| { |
| } |
| |
| sal_uLong DictionaryNeo::loadEntries(const OUString &rMainURL) |
| { |
| MutexGuard aGuard( GetLinguMutex() ); |
| |
| // counter check that it is safe to set bIsModified to sal_False at |
| // the end of the function |
| DBG_ASSERT(!bIsModified, "lng : dictionary already modified!"); |
| |
| // function should only be called once in order to load entries from file |
| bNeedEntries = sal_False; |
| |
| if (rMainURL.getLength() == 0) |
| return 0; |
| |
| uno::Reference< lang::XMultiServiceFactory > xServiceFactory( utl::getProcessServiceFactory() ); |
| |
| // get XInputStream stream |
| uno::Reference< io::XInputStream > xStream; |
| try |
| { |
| uno::Reference< ucb::XSimpleFileAccess > xAccess( xServiceFactory->createInstance( |
| A2OU( "com.sun.star.ucb.SimpleFileAccess" ) ), uno::UNO_QUERY_THROW ); |
| xStream = xAccess->openFileRead( rMainURL ); |
| } |
| catch (uno::Exception & e) |
| { |
| DBG_ASSERT( 0, "failed to get input stream" ); |
| (void) e; |
| } |
| if (!xStream.is()) |
| return static_cast< sal_uLong >(-1); |
| |
| SvStreamPtr pStream = SvStreamPtr( utl::UcbStreamHelper::CreateStream( xStream ) ); |
| |
| sal_uLong nErr = sal::static_int_cast< sal_uLong >(-1); |
| |
| // Header einlesen |
| sal_Bool bNegativ; |
| sal_uInt16 nLang; |
| nDicVersion = ReadDicVersion(pStream, nLang, bNegativ); |
| if (0 != (nErr = pStream->GetError())) |
| return nErr; |
| |
| nLanguage = nLang; |
| |
| eDicType = bNegativ ? DictionaryType_NEGATIVE : DictionaryType_POSITIVE; |
| |
| rtl_TextEncoding eEnc = osl_getThreadTextEncoding(); |
| if (nDicVersion >= DIC_VERSION_6) |
| eEnc = RTL_TEXTENCODING_UTF8; |
| nCount = 0; |
| |
| if (DIC_VERSION_6 == nDicVersion || |
| DIC_VERSION_5 == nDicVersion || |
| DIC_VERSION_2 == nDicVersion) |
| { |
| sal_uInt16 nLen = 0; |
| sal_Char aWordBuf[ BUFSIZE ]; |
| |
| // Das erste Wort einlesen |
| if (!pStream->IsEof()) |
| { |
| *pStream >> nLen; |
| if (0 != (nErr = pStream->GetError())) |
| return nErr; |
| if ( nLen < BUFSIZE ) |
| { |
| pStream->Read(aWordBuf, nLen); |
| if (0 != (nErr = pStream->GetError())) |
| return nErr; |
| *(aWordBuf + nLen) = 0; |
| } |
| } |
| |
| while(!pStream->IsEof()) |
| { |
| // Aus dem File einlesen |
| // Einfuegen ins Woerterbuch ohne Konvertierung |
| if(*aWordBuf) |
| { |
| ByteString aDummy( aWordBuf ); |
| String aText( aDummy, eEnc ); |
| uno::Reference< XDictionaryEntry > xEntry = |
| new DicEntry( aText, bNegativ ); |
| addEntry_Impl( xEntry , sal_True ); //! don't launch events here |
| } |
| |
| *pStream >> nLen; |
| if (pStream->IsEof()) // #75082# GPF in online-spelling |
| break; |
| if (0 != (nErr = pStream->GetError())) |
| return nErr; |
| #ifdef LINGU_EXCEPTIONS |
| if (nLen >= BUFSIZE) |
| throw io::IOException() ; |
| #endif |
| |
| if (nLen < BUFSIZE) |
| { |
| pStream->Read(aWordBuf, nLen); |
| if (0 != (nErr = pStream->GetError())) |
| return nErr; |
| } |
| else |
| return SVSTREAM_READ_ERROR; |
| *(aWordBuf + nLen) = 0; |
| } |
| } |
| else if (DIC_VERSION_7 == nDicVersion) |
| { |
| sal_Bool bSuccess; |
| ByteString aLine; |
| |
| // remaining lines - stock strings (a [==] b) |
| while (sal_True == (bSuccess = pStream->ReadLine(aLine))) |
| { |
| if (aLine.GetChar(0) == '#') // skip comments |
| continue; |
| rtl::OUString aText = rtl::OStringToOUString (aLine, RTL_TEXTENCODING_UTF8); |
| uno::Reference< XDictionaryEntry > xEntry = |
| new DicEntry( aText, eDicType == DictionaryType_NEGATIVE ); |
| addEntry_Impl( xEntry , sal_True ); //! don't launch events here |
| } |
| } |
| |
| DBG_ASSERT(isSorted(), "lng : dictionary is not sorted"); |
| |
| // since this routine should be called only initialy (prior to any |
| // modification to be saved) we reset the bIsModified flag here that |
| // was implicitly set by addEntry_Impl |
| bIsModified = sal_False; |
| |
| return pStream->GetError(); |
| } |
| |
| |
| static ByteString formatForSave( |
| const uno::Reference< XDictionaryEntry > &xEntry, rtl_TextEncoding eEnc ) |
| { |
| ByteString aStr(xEntry->getDictionaryWord().getStr(), eEnc); |
| |
| if (xEntry->isNegative()) |
| { |
| aStr += "=="; |
| aStr += ByteString(xEntry->getReplacementText().getStr(), eEnc); |
| } |
| return aStr; |
| } |
| |
| |
| sal_uLong DictionaryNeo::saveEntries(const OUString &rURL) |
| { |
| MutexGuard aGuard( GetLinguMutex() ); |
| |
| if (rURL.getLength() == 0) |
| return 0; |
| DBG_ASSERT(!INetURLObject( rURL ).HasError(), "lng : invalid URL"); |
| |
| uno::Reference< lang::XMultiServiceFactory > xServiceFactory( utl::getProcessServiceFactory() ); |
| |
| // get XOutputStream stream |
| uno::Reference< io::XStream > xStream; |
| try |
| { |
| uno::Reference< ucb::XSimpleFileAccess > xAccess( xServiceFactory->createInstance( |
| A2OU( "com.sun.star.ucb.SimpleFileAccess" ) ), uno::UNO_QUERY_THROW ); |
| xStream = xAccess->openFileReadWrite( rURL ); |
| } |
| catch (uno::Exception & e) |
| { |
| DBG_ASSERT( 0, "failed to get input stream" ); |
| (void) e; |
| } |
| if (!xStream.is()) |
| return static_cast< sal_uLong >(-1); |
| |
| SvStreamPtr pStream = SvStreamPtr( utl::UcbStreamHelper::CreateStream( xStream ) ); |
| sal_uLong nErr = sal::static_int_cast< sal_uLong >(-1); |
| |
| // |
| // Always write as the latest version, i.e. DIC_VERSION_7 |
| // |
| rtl_TextEncoding eEnc = RTL_TEXTENCODING_UTF8; |
| pStream->WriteLine(ByteString (pVerOOo7)); |
| if (0 != (nErr = pStream->GetError())) |
| return nErr; |
| if (nLanguage == LANGUAGE_NONE) |
| pStream->WriteLine(ByteString("lang: <none>")); |
| else |
| { |
| ByteString aLine("lang: "); |
| aLine += ByteString( String( MsLangId::convertLanguageToIsoString( nLanguage ) ), eEnc); |
| pStream->WriteLine( aLine ); |
| } |
| if (0 != (nErr = pStream->GetError())) |
| return nErr; |
| if (eDicType == DictionaryType_POSITIVE) |
| pStream->WriteLine(ByteString("type: positive")); |
| else |
| pStream->WriteLine(ByteString("type: negative")); |
| if (0 != (nErr = pStream->GetError())) |
| return nErr; |
| pStream->WriteLine(ByteString("---")); |
| if (0 != (nErr = pStream->GetError())) |
| return nErr; |
| const uno::Reference< XDictionaryEntry > *pEntry = aEntries.getConstArray(); |
| for (sal_Int32 i = 0; i < nCount; i++) |
| { |
| ByteString aOutStr = formatForSave(pEntry[i], eEnc); |
| pStream->WriteLine (aOutStr); |
| if (0 != (nErr = pStream->GetError())) |
| return nErr; |
| } |
| |
| //If we are migrating from an older version, then on first successful |
| //write, we're now converted to the latest version, i.e. DIC_VERSION_7 |
| nDicVersion = DIC_VERSION_7; |
| |
| return nErr; |
| } |
| |
| void DictionaryNeo::launchEvent(sal_Int16 nEvent, |
| uno::Reference< XDictionaryEntry > xEntry) |
| { |
| MutexGuard aGuard( GetLinguMutex() ); |
| |
| DictionaryEvent aEvt; |
| aEvt.Source = uno::Reference< XDictionary >( this ); |
| aEvt.nEvent = nEvent; |
| aEvt.xDictionaryEntry = xEntry; |
| |
| cppu::OInterfaceIteratorHelper aIt( aDicEvtListeners ); |
| while (aIt.hasMoreElements()) |
| { |
| uno::Reference< XDictionaryEventListener > xRef( aIt.next(), UNO_QUERY ); |
| if (xRef.is()) |
| xRef->processDictionaryEvent( aEvt ); |
| } |
| } |
| |
| int DictionaryNeo::cmpDicEntry(const OUString& rWord1, |
| const OUString &rWord2, |
| sal_Bool bSimilarOnly) |
| { |
| MutexGuard aGuard( GetLinguMutex() ); |
| |
| // returns 0 if rWord1 is equal to rWord2 |
| // " a value < 0 if rWord1 is less than rWord2 |
| // " a value > 0 if rWord1 is greater than rWord2 |
| |
| int nRes = 0; |
| |
| OUString aWord1( rWord1 ), |
| aWord2( rWord2 ); |
| sal_Int32 nLen1 = aWord1.getLength(), |
| nLen2 = aWord2.getLength(); |
| if (bSimilarOnly) |
| { |
| const sal_Unicode cChar = '.'; |
| if (nLen1 && cChar == aWord1[ nLen1 - 1 ]) |
| nLen1--; |
| if (nLen2 && cChar == aWord2[ nLen2 - 1 ]) |
| nLen2--; |
| } |
| |
| const sal_Unicode cIgnChar = '='; |
| sal_Int32 nIdx1 = 0, |
| nIdx2 = 0, |
| nNumIgnChar1 = 0, |
| nNumIgnChar2 = 0; |
| |
| sal_Int32 nDiff = 0; |
| sal_Unicode cChar1 = '\0'; |
| sal_Unicode cChar2 = '\0'; |
| do |
| { |
| // skip chars to be ignored |
| while (nIdx1 < nLen1 && (cChar1 = aWord1[ nIdx1 ]) == cIgnChar) |
| { |
| nIdx1++; |
| nNumIgnChar1++; |
| } |
| while (nIdx2 < nLen2 && (cChar2 = aWord2[ nIdx2 ]) == cIgnChar) |
| { |
| nIdx2++; |
| nNumIgnChar2++; |
| } |
| |
| if (nIdx1 < nLen1 && nIdx2 < nLen2) |
| { |
| nDiff = cChar1 - cChar2; |
| if (nDiff) |
| break; |
| nIdx1++; |
| nIdx2++; |
| } |
| } while (nIdx1 < nLen1 && nIdx2 < nLen2); |
| |
| |
| if (nDiff) |
| nRes = nDiff; |
| else |
| { // the string with the smallest count of not ignored chars is the |
| // shorter one |
| |
| // count remaining IgnChars |
| while (nIdx1 < nLen1 ) |
| { |
| if (aWord1[ nIdx1++ ] == cIgnChar) |
| nNumIgnChar1++; |
| } |
| while (nIdx2 < nLen2 ) |
| { |
| if (aWord2[ nIdx2++ ] == cIgnChar) |
| nNumIgnChar2++; |
| } |
| |
| nRes = ((sal_Int32) nLen1 - nNumIgnChar1) - ((sal_Int32) nLen2 - nNumIgnChar2); |
| } |
| |
| return nRes; |
| } |
| |
| sal_Bool DictionaryNeo::seekEntry(const OUString &rWord, |
| sal_Int32 *pPos, sal_Bool bSimilarOnly) |
| { |
| // look for entry with binary search. |
| // return sal_True if found sal_False else. |
| // if pPos != NULL it will become the position of the found entry, or |
| // if that was not found the position where it has to be inserted |
| // to keep the entries sorted |
| |
| MutexGuard aGuard( GetLinguMutex() ); |
| |
| const uno::Reference< XDictionaryEntry > *pEntry = aEntries.getConstArray(); |
| sal_Int32 nUpperIdx = getCount(), |
| nMidIdx, |
| nLowerIdx = 0; |
| if( nUpperIdx > 0 ) |
| { |
| nUpperIdx--; |
| while( nLowerIdx <= nUpperIdx ) |
| { |
| nMidIdx = (nLowerIdx + nUpperIdx) / 2; |
| DBG_ASSERT(pEntry[nMidIdx].is(), "lng : empty entry encountered"); |
| |
| int nCmp = - cmpDicEntry( pEntry[nMidIdx]->getDictionaryWord(), |
| rWord, bSimilarOnly ); |
| if(nCmp == 0) |
| { |
| if( pPos ) *pPos = nMidIdx; |
| return sal_True; |
| } |
| else if(nCmp > 0) |
| nLowerIdx = nMidIdx + 1; |
| else if( nMidIdx == 0 ) |
| { |
| if( pPos ) *pPos = nLowerIdx; |
| return sal_False; |
| } |
| else |
| nUpperIdx = nMidIdx - 1; |
| } |
| } |
| if( pPos ) *pPos = nLowerIdx; |
| return sal_False; |
| } |
| |
| sal_Bool DictionaryNeo::isSorted() |
| { |
| sal_Bool bRes = sal_True; |
| |
| const uno::Reference< XDictionaryEntry > *pEntry = aEntries.getConstArray(); |
| sal_Int32 nEntries = getCount(); |
| sal_Int32 i; |
| for (i = 1; i < nEntries; i++) |
| { |
| if (cmpDicEntry( pEntry[i-1]->getDictionaryWord(), |
| pEntry[i]->getDictionaryWord() ) > 0) |
| { |
| bRes = sal_False; |
| break; |
| } |
| } |
| return bRes; |
| } |
| |
| sal_Bool DictionaryNeo::addEntry_Impl(const uno::Reference< XDictionaryEntry > xDicEntry, |
| sal_Bool bIsLoadEntries) |
| { |
| MutexGuard aGuard( GetLinguMutex() ); |
| |
| sal_Bool bRes = sal_False; |
| |
| if ( bIsLoadEntries || (!bIsReadonly && xDicEntry.is()) ) |
| { |
| sal_Bool bIsNegEntry = xDicEntry->isNegative(); |
| sal_Bool bAddEntry = !isFull() && |
| ( ( eDicType == DictionaryType_POSITIVE && !bIsNegEntry ) |
| || ( eDicType == DictionaryType_NEGATIVE && bIsNegEntry ) |
| || ( eDicType == DictionaryType_MIXED ) ); |
| |
| // look for position to insert entry at |
| // if there is already an entry do not insert the new one |
| sal_Int32 nPos = 0; |
| sal_Bool bFound = sal_False; |
| if (bAddEntry) |
| { |
| bFound = seekEntry( xDicEntry->getDictionaryWord(), &nPos ); |
| if (bFound) |
| bAddEntry = sal_False; |
| } |
| |
| if (bAddEntry) |
| { |
| DBG_ASSERT(!bNeedEntries, "lng : entries still not loaded"); |
| |
| if (nCount >= aEntries.getLength()) |
| aEntries.realloc( Max(2 * nCount, nCount + 32) ); |
| uno::Reference< XDictionaryEntry > *pEntry = aEntries.getArray(); |
| |
| // shift old entries right |
| sal_Int32 i; |
| for (i = nCount - 1; i >= nPos; i--) |
| pEntry[ i+1 ] = pEntry[ i ]; |
| // insert new entry at specified position |
| pEntry[ nPos ] = xDicEntry; |
| DBG_ASSERT(isSorted(), "lng : dictionary entries unsorted"); |
| |
| nCount++; |
| |
| bIsModified = sal_True; |
| bRes = sal_True; |
| |
| if (!bIsLoadEntries) |
| launchEvent( DictionaryEventFlags::ADD_ENTRY, xDicEntry ); |
| } |
| } |
| |
| return bRes; |
| } |
| |
| |
| uno::Reference< XInterface > SAL_CALL DictionaryNeo_CreateInstance( |
| const uno::Reference< XMultiServiceFactory > & /*rSMgr*/ ) |
| throw(Exception) |
| { |
| uno::Reference< XInterface > xService = |
| (cppu::OWeakObject*) new DictionaryNeo; |
| return xService; |
| } |
| |
| OUString SAL_CALL DictionaryNeo::getName( ) |
| throw(RuntimeException) |
| { |
| MutexGuard aGuard( GetLinguMutex() ); |
| return aDicName; |
| } |
| |
| void SAL_CALL DictionaryNeo::setName( const OUString& aName ) |
| throw(RuntimeException) |
| { |
| MutexGuard aGuard( GetLinguMutex() ); |
| |
| if (aDicName != aName) |
| { |
| aDicName = aName; |
| launchEvent(DictionaryEventFlags::CHG_NAME, NULL); |
| } |
| } |
| |
| DictionaryType SAL_CALL DictionaryNeo::getDictionaryType( ) |
| throw(RuntimeException) |
| { |
| MutexGuard aGuard( GetLinguMutex() ); |
| |
| return eDicType; |
| } |
| |
| void SAL_CALL DictionaryNeo::setActive( sal_Bool bActivate ) |
| throw(RuntimeException) |
| { |
| MutexGuard aGuard( GetLinguMutex() ); |
| |
| if (bIsActive != bActivate) |
| { |
| bIsActive = bActivate != 0; |
| sal_Int16 nEvent = bIsActive ? |
| DictionaryEventFlags::ACTIVATE_DIC : DictionaryEventFlags::DEACTIVATE_DIC; |
| |
| // remove entries from memory if dictionary is deactivated |
| if (bIsActive == sal_False) |
| { |
| sal_Bool bIsEmpty = nCount == 0; |
| |
| // save entries first if necessary |
| if (bIsModified && hasLocation() && !isReadonly()) |
| { |
| store(); |
| |
| aEntries.realloc( 0 ); |
| nCount = 0; |
| bNeedEntries = !bIsEmpty; |
| } |
| DBG_ASSERT( !bIsModified || !hasLocation() || isReadonly(), |
| "lng : dictionary is still modified" ); |
| } |
| |
| launchEvent(nEvent, NULL); |
| } |
| } |
| |
| sal_Bool SAL_CALL DictionaryNeo::isActive( ) |
| throw(RuntimeException) |
| { |
| MutexGuard aGuard( GetLinguMutex() ); |
| return bIsActive; |
| } |
| |
| sal_Int32 SAL_CALL DictionaryNeo::getCount( ) |
| throw(RuntimeException) |
| { |
| MutexGuard aGuard( GetLinguMutex() ); |
| |
| if (bNeedEntries) |
| loadEntries( aMainURL ); |
| return nCount; |
| } |
| |
| Locale SAL_CALL DictionaryNeo::getLocale( ) |
| throw(RuntimeException) |
| { |
| MutexGuard aGuard( GetLinguMutex() ); |
| Locale aRes; |
| return LanguageToLocale( aRes, nLanguage ); |
| } |
| |
| void SAL_CALL DictionaryNeo::setLocale( const Locale& aLocale ) |
| throw(RuntimeException) |
| { |
| MutexGuard aGuard( GetLinguMutex() ); |
| sal_Int16 nLanguageP = LocaleToLanguage( aLocale ); |
| if (!bIsReadonly && nLanguage != nLanguageP) |
| { |
| nLanguage = nLanguageP; |
| bIsModified = sal_True; // new language needs to be saved with dictionary |
| |
| launchEvent( DictionaryEventFlags::CHG_LANGUAGE, NULL ); |
| } |
| } |
| |
| uno::Reference< XDictionaryEntry > SAL_CALL DictionaryNeo::getEntry( |
| const OUString& aWord ) |
| throw(RuntimeException) |
| { |
| MutexGuard aGuard( GetLinguMutex() ); |
| |
| if (bNeedEntries) |
| loadEntries( aMainURL ); |
| |
| sal_Int32 nPos; |
| sal_Bool bFound = seekEntry( aWord, &nPos, sal_True ); |
| DBG_ASSERT( nCount <= aEntries.getLength(), "lng : wrong number of entries"); |
| DBG_ASSERT(!bFound || nPos < nCount, "lng : index out of range"); |
| |
| return bFound ? aEntries.getConstArray()[ nPos ] |
| : uno::Reference< XDictionaryEntry >(); |
| } |
| |
| sal_Bool SAL_CALL DictionaryNeo::addEntry( |
| const uno::Reference< XDictionaryEntry >& xDicEntry ) |
| throw(RuntimeException) |
| { |
| MutexGuard aGuard( GetLinguMutex() ); |
| |
| sal_Bool bRes = sal_False; |
| |
| if (!bIsReadonly) |
| { |
| if (bNeedEntries) |
| loadEntries( aMainURL ); |
| bRes = addEntry_Impl( xDicEntry ); |
| } |
| |
| return bRes; |
| } |
| |
| sal_Bool SAL_CALL |
| DictionaryNeo::add( const OUString& rWord, sal_Bool bIsNegative, |
| const OUString& rRplcText ) |
| throw(RuntimeException) |
| { |
| MutexGuard aGuard( GetLinguMutex() ); |
| |
| sal_Bool bRes = sal_False; |
| |
| if (!bIsReadonly) |
| { |
| uno::Reference< XDictionaryEntry > xEntry = |
| new DicEntry( rWord, bIsNegative, rRplcText ); |
| bRes = addEntry_Impl( xEntry ); |
| } |
| |
| return bRes; |
| } |
| |
| void lcl_SequenceRemoveElementAt( |
| uno::Sequence< uno::Reference< XDictionaryEntry > >& rEntries, int nPos ) |
| { |
| //TODO: helper for SequenceRemoveElementAt available? |
| if(nPos >= rEntries.getLength()) |
| return; |
| uno::Sequence< uno::Reference< XDictionaryEntry > > aTmp(rEntries.getLength() - 1); |
| uno::Reference< XDictionaryEntry > * pOrig = rEntries.getArray(); |
| uno::Reference< XDictionaryEntry > * pTemp = aTmp.getArray(); |
| int nOffset = 0; |
| for(int i = 0; i < aTmp.getLength(); i++) |
| { |
| if(nPos == i) |
| nOffset++; |
| pTemp[i] = pOrig[i + nOffset]; |
| } |
| |
| rEntries = aTmp; |
| } |
| |
| sal_Bool SAL_CALL DictionaryNeo::remove( const OUString& aWord ) |
| throw(RuntimeException) |
| { |
| MutexGuard aGuard( GetLinguMutex() ); |
| |
| sal_Bool bRemoved = sal_False; |
| |
| if (!bIsReadonly) |
| { |
| if (bNeedEntries) |
| loadEntries( aMainURL ); |
| |
| sal_Int32 nPos; |
| sal_Bool bFound = seekEntry( aWord, &nPos ); |
| DBG_ASSERT( nCount < aEntries.getLength(), |
| "lng : wrong number of entries"); |
| DBG_ASSERT(!bFound || nPos < nCount, "lng : index out of range"); |
| |
| // remove element if found |
| if (bFound) |
| { |
| // entry to be removed |
| uno::Reference< XDictionaryEntry > |
| xDicEntry( aEntries.getConstArray()[ nPos ] ); |
| DBG_ASSERT(xDicEntry.is(), "lng : dictionary entry is NULL"); |
| |
| nCount--; |
| |
| //! the following call reduces the length of the sequence by 1 also |
| lcl_SequenceRemoveElementAt( aEntries, nPos ); |
| bRemoved = bIsModified = sal_True; |
| |
| launchEvent( DictionaryEventFlags::DEL_ENTRY, xDicEntry ); |
| } |
| } |
| |
| return bRemoved; |
| } |
| |
| sal_Bool SAL_CALL DictionaryNeo::isFull( ) |
| throw(RuntimeException) |
| { |
| MutexGuard aGuard( GetLinguMutex() ); |
| |
| if (bNeedEntries) |
| loadEntries( aMainURL ); |
| return nCount >= DIC_MAX_ENTRIES; |
| } |
| |
| uno::Sequence< uno::Reference< XDictionaryEntry > > |
| SAL_CALL DictionaryNeo::getEntries( ) |
| throw(RuntimeException) |
| { |
| MutexGuard aGuard( GetLinguMutex() ); |
| |
| if (bNeedEntries) |
| loadEntries( aMainURL ); |
| //! return sequence with length equal to the number of dictionary entries |
| //! (internal used sequence may have additional unused elements.) |
| return uno::Sequence< uno::Reference< XDictionaryEntry > > |
| (aEntries.getConstArray(), nCount); |
| } |
| |
| |
| void SAL_CALL DictionaryNeo::clear( ) |
| throw(RuntimeException) |
| { |
| MutexGuard aGuard( GetLinguMutex() ); |
| |
| if (!bIsReadonly && nCount) |
| { |
| // release all references to old entries and provide space for new ones |
| aEntries = uno::Sequence< uno::Reference< XDictionaryEntry > > ( 32 ); |
| |
| nCount = 0; |
| bNeedEntries = sal_False; |
| bIsModified = sal_True; |
| |
| launchEvent( DictionaryEventFlags::ENTRIES_CLEARED , NULL ); |
| } |
| } |
| |
| sal_Bool SAL_CALL DictionaryNeo::addDictionaryEventListener( |
| const uno::Reference< XDictionaryEventListener >& xListener ) |
| throw(RuntimeException) |
| { |
| MutexGuard aGuard( GetLinguMutex() ); |
| |
| sal_Bool bRes = sal_False; |
| if (xListener.is()) |
| { |
| sal_Int32 nLen = aDicEvtListeners.getLength(); |
| bRes = aDicEvtListeners.addInterface( xListener ) != nLen; |
| } |
| return bRes; |
| } |
| |
| sal_Bool SAL_CALL DictionaryNeo::removeDictionaryEventListener( |
| const uno::Reference< XDictionaryEventListener >& xListener ) |
| throw(RuntimeException) |
| { |
| MutexGuard aGuard( GetLinguMutex() ); |
| |
| sal_Bool bRes = sal_False; |
| if (xListener.is()) |
| { |
| sal_Int32 nLen = aDicEvtListeners.getLength(); |
| bRes = aDicEvtListeners.removeInterface( xListener ) != nLen; |
| } |
| return bRes; |
| } |
| |
| |
| sal_Bool SAL_CALL DictionaryNeo::hasLocation() |
| throw(RuntimeException) |
| { |
| MutexGuard aGuard( GetLinguMutex() ); |
| return aMainURL.getLength() > 0; |
| } |
| |
| OUString SAL_CALL DictionaryNeo::getLocation() |
| throw(RuntimeException) |
| { |
| MutexGuard aGuard( GetLinguMutex() ); |
| return aMainURL; |
| } |
| |
| sal_Bool SAL_CALL DictionaryNeo::isReadonly() |
| throw(RuntimeException) |
| { |
| MutexGuard aGuard( GetLinguMutex() ); |
| |
| return bIsReadonly; |
| } |
| |
| void SAL_CALL DictionaryNeo::store() |
| throw(io::IOException, RuntimeException) |
| { |
| MutexGuard aGuard( GetLinguMutex() ); |
| |
| if (bIsModified && hasLocation() && !isReadonly()) |
| { |
| if (saveEntries( aMainURL )) |
| { |
| #ifdef LINGU_EXCEPTIONS |
| throw io::IOException(); |
| #endif |
| } |
| else |
| bIsModified = sal_False; |
| } |
| } |
| |
| void SAL_CALL DictionaryNeo::storeAsURL( |
| const OUString& aURL, |
| const uno::Sequence< beans::PropertyValue >& /*rArgs*/ ) |
| throw(io::IOException, RuntimeException) |
| { |
| MutexGuard aGuard( GetLinguMutex() ); |
| |
| if (saveEntries( aURL )) |
| { |
| #ifdef LINGU_EXCEPTIONS |
| throw io::IOException(); |
| #endif |
| } |
| else |
| { |
| aMainURL = aURL; |
| bIsModified = sal_False; |
| bIsReadonly = IsReadOnly( getLocation() ); |
| } |
| } |
| |
| void SAL_CALL DictionaryNeo::storeToURL( |
| const OUString& aURL, |
| const uno::Sequence< beans::PropertyValue >& /*rArgs*/ ) |
| throw(io::IOException, RuntimeException) |
| { |
| MutexGuard aGuard( GetLinguMutex() ); |
| |
| if (saveEntries( aURL )) |
| { |
| #ifdef LINGU_EXCEPTIONS |
| throw io::IOException(); |
| #endif |
| } |
| } |
| |
| /////////////////////////////////////////////////////////////////////////// |
| |
| DicEntry::DicEntry() |
| { |
| bIsNegativ = sal_False; |
| } |
| |
| DicEntry::DicEntry(const OUString &rDicFileWord, |
| sal_Bool bIsNegativWord) |
| { |
| if (rDicFileWord.getLength()) |
| splitDicFileWord( rDicFileWord, aDicWord, aReplacement ); |
| bIsNegativ = bIsNegativWord; |
| } |
| |
| DicEntry::DicEntry(const OUString &rDicWord, sal_Bool bNegativ, |
| const OUString &rRplcText) : |
| aDicWord (rDicWord), |
| aReplacement (rRplcText), |
| bIsNegativ (bNegativ) |
| { |
| } |
| |
| DicEntry::~DicEntry() |
| { |
| } |
| |
| void DicEntry::splitDicFileWord(const OUString &rDicFileWord, |
| OUString &rDicWord, |
| OUString &rReplacement) |
| { |
| MutexGuard aGuard( GetLinguMutex() ); |
| |
| static const OUString aDelim( A2OU( "==" ) ); |
| |
| sal_Int32 nDelimPos = rDicFileWord.indexOf( aDelim ); |
| if (-1 != nDelimPos) |
| { |
| sal_Int32 nTriplePos = nDelimPos + 2; |
| if ( nTriplePos < rDicFileWord.getLength() |
| && rDicFileWord[ nTriplePos ] == '=' ) |
| ++nDelimPos; |
| rDicWord = rDicFileWord.copy( 0, nDelimPos ); |
| rReplacement = rDicFileWord.copy( nDelimPos + 2 ); |
| } |
| else |
| { |
| rDicWord = rDicFileWord; |
| rReplacement = OUString(); |
| } |
| } |
| |
| OUString SAL_CALL DicEntry::getDictionaryWord( ) |
| throw(RuntimeException) |
| { |
| MutexGuard aGuard( GetLinguMutex() ); |
| return aDicWord; |
| } |
| |
| sal_Bool SAL_CALL DicEntry::isNegative( ) |
| throw(RuntimeException) |
| { |
| MutexGuard aGuard( GetLinguMutex() ); |
| return bIsNegativ; |
| } |
| |
| OUString SAL_CALL DicEntry::getReplacementText( ) |
| throw(RuntimeException) |
| { |
| MutexGuard aGuard( GetLinguMutex() ); |
| return aReplacement; |
| } |
| |
| |
| /////////////////////////////////////////////////////////////////////////// |
| |