| /************************************************************** |
| * |
| * 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. |
| * |
| *************************************************************/ |
| |
| |
| #include "precompiled_linguistic.hxx" |
| |
| #include <com/sun/star/container/XContentEnumerationAccess.hpp> |
| #include <com/sun/star/container/XEnumeration.hpp> |
| #include <com/sun/star/container/XNameAccess.hpp> |
| #include <com/sun/star/container/XNameContainer.hpp> |
| #include <com/sun/star/container/XNameReplace.hpp> |
| #include <com/sun/star/i18n/XBreakIterator.hpp> |
| #include <com/sun/star/lang/XComponent.hpp> |
| #include <com/sun/star/lang/XServiceInfo.hpp> |
| #include <com/sun/star/lang/XMultiServiceFactory.hpp> |
| #include <com/sun/star/linguistic2/XSupportedLocales.hpp> |
| #include <com/sun/star/linguistic2/XProofreader.hpp> |
| #include <com/sun/star/linguistic2/XProofreadingIterator.hpp> |
| #include <com/sun/star/linguistic2/SingleProofreadingError.hpp> |
| #include <com/sun/star/linguistic2/ProofreadingResult.hpp> |
| #include <com/sun/star/linguistic2/LinguServiceEvent.hpp> |
| #include <com/sun/star/linguistic2/LinguServiceEventFlags.hpp> |
| #include <com/sun/star/registry/XRegistryKey.hpp> |
| #include <com/sun/star/text/TextMarkupType.hpp> |
| #include <com/sun/star/text/TextMarkupDescriptor.hpp> |
| #include <com/sun/star/text/XTextMarkup.hpp> |
| #include <com/sun/star/text/XMultiTextMarkup.hpp> |
| #include <com/sun/star/text/XFlatParagraph.hpp> |
| #include <com/sun/star/text/XFlatParagraphIterator.hpp> |
| #include <com/sun/star/uno/XComponentContext.hpp> |
| #include <com/sun/star/lang/XSingleComponentFactory.hpp> |
| |
| #include <sal/config.h> |
| #include <osl/conditn.hxx> |
| #include <osl/thread.hxx> |
| #include <cppuhelper/implbase4.hxx> |
| #include <cppuhelper/implementationentry.hxx> |
| #include <cppuhelper/interfacecontainer.h> |
| #include <cppuhelper/factory.hxx> |
| #include <i18npool/mslangid.hxx> |
| #include <unotools/processfactory.hxx> |
| #include <comphelper/extract.hxx> |
| |
| #include <deque> |
| #include <map> |
| #include <vector> |
| |
| #include "linguistic/misc.hxx" |
| #include "defs.hxx" |
| #include "lngopt.hxx" |
| |
| #include "gciterator.hxx" |
| |
| using ::rtl::OUString; |
| using namespace linguistic; |
| using namespace ::com::sun::star; |
| |
| // forward declarations |
| static ::rtl::OUString GrammarCheckingIterator_getImplementationName() throw(); |
| static uno::Sequence< OUString > GrammarCheckingIterator_getSupportedServiceNames() throw(); |
| |
| |
| ////////////////////////////////////////////////////////////////////// |
| |
| // white space list: obtained from the fonts.config.txt of a Linux system. |
| static sal_Unicode aWhiteSpaces[] = |
| { |
| 0x0020, /* SPACE */ |
| 0x00a0, /* NO-BREAK SPACE */ |
| 0x00ad, /* SOFT HYPHEN */ |
| 0x115f, /* HANGUL CHOSEONG FILLER */ |
| 0x1160, /* HANGUL JUNGSEONG FILLER */ |
| 0x1680, /* OGHAM SPACE MARK */ |
| 0x2000, /* EN QUAD */ |
| 0x2001, /* EM QUAD */ |
| 0x2002, /* EN SPACE */ |
| 0x2003, /* EM SPACE */ |
| 0x2004, /* THREE-PER-EM SPACE */ |
| 0x2005, /* FOUR-PER-EM SPACE */ |
| 0x2006, /* SIX-PER-EM SPACE */ |
| 0x2007, /* FIGURE SPACE */ |
| 0x2008, /* PUNCTUATION SPACE */ |
| 0x2009, /* THIN SPACE */ |
| 0x200a, /* HAIR SPACE */ |
| 0x200b, /* ZERO WIDTH SPACE */ |
| 0x200c, /* ZERO WIDTH NON-JOINER */ |
| 0x200d, /* ZERO WIDTH JOINER */ |
| 0x200e, /* LEFT-TO-RIGHT MARK */ |
| 0x200f, /* RIGHT-TO-LEFT MARK */ |
| 0x2028, /* LINE SEPARATOR */ |
| 0x2029, /* PARAGRAPH SEPARATOR */ |
| 0x202a, /* LEFT-TO-RIGHT EMBEDDING */ |
| 0x202b, /* RIGHT-TO-LEFT EMBEDDING */ |
| 0x202c, /* POP DIRECTIONAL FORMATTING */ |
| 0x202d, /* LEFT-TO-RIGHT OVERRIDE */ |
| 0x202e, /* RIGHT-TO-LEFT OVERRIDE */ |
| 0x202f, /* NARROW NO-BREAK SPACE */ |
| 0x205f, /* MEDIUM MATHEMATICAL SPACE */ |
| 0x2060, /* WORD JOINER */ |
| 0x2061, /* FUNCTION APPLICATION */ |
| 0x2062, /* INVISIBLE TIMES */ |
| 0x2063, /* INVISIBLE SEPARATOR */ |
| 0x206A, /* INHIBIT SYMMETRIC SWAPPING */ |
| 0x206B, /* ACTIVATE SYMMETRIC SWAPPING */ |
| 0x206C, /* INHIBIT ARABIC FORM SHAPING */ |
| 0x206D, /* ACTIVATE ARABIC FORM SHAPING */ |
| 0x206E, /* NATIONAL DIGIT SHAPES */ |
| 0x206F, /* NOMINAL DIGIT SHAPES */ |
| 0x3000, /* IDEOGRAPHIC SPACE */ |
| 0x3164, /* HANGUL FILLER */ |
| 0xfeff, /* ZERO WIDTH NO-BREAK SPACE */ |
| 0xffa0, /* HALFWIDTH HANGUL FILLER */ |
| 0xfff9, /* INTERLINEAR ANNOTATION ANCHOR */ |
| 0xfffa, /* INTERLINEAR ANNOTATION SEPARATOR */ |
| 0xfffb /* INTERLINEAR ANNOTATION TERMINATOR */ |
| }; |
| |
| static int nWhiteSpaces = sizeof( aWhiteSpaces ) / sizeof( aWhiteSpaces[0] ); |
| |
| static bool lcl_IsWhiteSpace( sal_Unicode cChar ) |
| { |
| bool bFound = false; |
| for (int i = 0; i < nWhiteSpaces && !bFound; ++i) |
| { |
| if (cChar == aWhiteSpaces[i]) |
| bFound = true; |
| } |
| return bFound; |
| } |
| |
| static sal_Int32 lcl_SkipWhiteSpaces( const OUString &rText, sal_Int32 nStartPos ) |
| { |
| // note having nStartPos point right behind the string is OK since that one |
| // is a correct end-of-sentence position to be returned from a grammar checker... |
| |
| const sal_Int32 nLen = rText.getLength(); |
| bool bIllegalArgument = false; |
| if (nStartPos < 0) |
| { |
| bIllegalArgument = true; |
| nStartPos = 0; |
| } |
| if (nStartPos > nLen) |
| { |
| bIllegalArgument = true; |
| nStartPos = nLen; |
| } |
| if (bIllegalArgument) |
| { |
| DBG_ASSERT( 0, "lcl_SkipWhiteSpaces: illegal arguments" ); |
| } |
| |
| sal_Int32 nRes = nStartPos; |
| if (0 <= nStartPos && nStartPos < nLen) |
| { |
| const sal_Unicode *pText = rText.getStr() + nStartPos; |
| while (nStartPos < nLen && lcl_IsWhiteSpace( *pText )) |
| ++pText; |
| nRes = pText - rText.getStr(); |
| } |
| |
| DBG_ASSERT( 0 <= nRes && nRes <= nLen, "lcl_SkipWhiteSpaces return value out of range" ); |
| return nRes; |
| } |
| |
| static sal_Int32 lcl_BacktraceWhiteSpaces( const OUString &rText, sal_Int32 nStartPos ) |
| { |
| // note: having nStartPos point right behind the string is OK since that one |
| // is a correct end-of-sentence position to be returned from a grammar checker... |
| |
| const sal_Int32 nLen = rText.getLength(); |
| bool bIllegalArgument = false; |
| if (nStartPos < 0) |
| { |
| bIllegalArgument = true; |
| nStartPos = 0; |
| } |
| if (nStartPos > nLen) |
| { |
| bIllegalArgument = true; |
| nStartPos = nLen; |
| } |
| if (bIllegalArgument) |
| { |
| DBG_ASSERT( 0, "lcl_BacktraceWhiteSpaces: illegal arguments" ); |
| } |
| |
| sal_Int32 nRes = nStartPos; |
| sal_Int32 nPosBefore = nStartPos - 1; |
| const sal_Unicode *pStart = rText.getStr(); |
| if (0 <= nPosBefore && nPosBefore < nLen && lcl_IsWhiteSpace( pStart[ nPosBefore ] )) |
| { |
| nStartPos = nPosBefore; |
| if (0 <= nStartPos && nStartPos < nLen) |
| { |
| const sal_Unicode *pText = rText.getStr() + nStartPos; |
| while (pText > pStart && lcl_IsWhiteSpace( *pText )) |
| --pText; |
| // now add 1 since we want to point to the first char after the last char in the sentence... |
| nRes = pText - pStart + 1; |
| } |
| } |
| |
| DBG_ASSERT( 0 <= nRes && nRes <= nLen, "lcl_BacktraceWhiteSpaces return value out of range" ); |
| return nRes; |
| } |
| |
| ////////////////////////////////////////////////////////////////////// |
| |
| extern "C" void workerfunc (void * gci) |
| { |
| ((GrammarCheckingIterator*)gci)->DequeueAndCheck(); |
| } |
| |
| static lang::Locale lcl_GetPrimaryLanguageOfSentence( |
| uno::Reference< text::XFlatParagraph > xFlatPara, |
| sal_Int32 nStartIndex ) |
| { |
| //get the language of the first word |
| return xFlatPara->getLanguageOfText( nStartIndex, 1 ); |
| } |
| |
| ////////////////////////////////////////////////////////////////////// |
| /* |
| class MyThread : punlic osl::Thread |
| { |
| void run () |
| { |
| DequeueAndCheck(); |
| } |
| |
| void own_terminate () |
| { |
| m_bEnd = true; |
| wait (3000); |
| terminate (); |
| } |
| } |
| |
| MyThread m_aQueue; |
| |
| vois startGrammarChecking() |
| { |
| if (!m_aQueue.isRunning ()) |
| m_aQueue.create (); |
| } |
| |
| void stopGrammarChecking () |
| { |
| if (m_aQueue.isRunning ()) |
| m_aQueue.own_terminate (); |
| } |
| */ |
| |
| GrammarCheckingIterator::GrammarCheckingIterator( const uno::Reference< lang::XMultiServiceFactory > & rxMgr ) : |
| m_xMSF( rxMgr ), |
| m_bEnd( sal_False ), |
| m_aCurCheckedDocId(), |
| m_bGCServicesChecked( sal_False ), |
| m_nDocIdCounter( 0 ), |
| m_nLastEndOfSentencePos( -1 ), |
| m_aEventListeners( MyMutex::get() ), |
| m_aNotifyListeners( MyMutex::get() ) |
| { |
| osl_createThread( workerfunc, this ); |
| } |
| |
| |
| GrammarCheckingIterator::~GrammarCheckingIterator() |
| { |
| ::osl::Guard< ::osl::Mutex > aGuard( MyMutex::get() ); |
| } |
| |
| |
| sal_Int32 GrammarCheckingIterator::NextDocId() |
| { |
| ::osl::Guard< ::osl::Mutex > aGuard( MyMutex::get() ); |
| m_nDocIdCounter += 1; |
| return m_nDocIdCounter; |
| } |
| |
| |
| OUString GrammarCheckingIterator::GetOrCreateDocId( |
| const uno::Reference< lang::XComponent > &xComponent ) |
| { |
| // internal method; will always be called with locked mutex |
| |
| OUString aRes; |
| if (xComponent.is()) |
| { |
| if (m_aDocIdMap.find( xComponent.get() ) != m_aDocIdMap.end()) |
| { |
| // return already existing entry |
| aRes = m_aDocIdMap[ xComponent.get() ]; |
| } |
| else // add new entry |
| { |
| sal_Int32 nRes = NextDocId(); |
| aRes = OUString::valueOf( nRes ); |
| m_aDocIdMap[ xComponent.get() ] = aRes; |
| xComponent->addEventListener( this ); |
| } |
| } |
| return aRes; |
| } |
| |
| |
| void GrammarCheckingIterator::AddEntry( |
| uno::WeakReference< text::XFlatParagraphIterator > xFlatParaIterator, |
| uno::WeakReference< text::XFlatParagraph > xFlatPara, |
| const OUString & rDocId, |
| sal_Int32 nStartIndex, |
| sal_Bool bAutomatic ) |
| { |
| // we may not need/have a xFlatParaIterator (e.g. if checkGrammarAtPos was called) |
| // but we always need a xFlatPara... |
| uno::Reference< text::XFlatParagraph > xPara( xFlatPara ); |
| if (xPara.is()) |
| { |
| FPEntry aNewFPEntry; |
| aNewFPEntry.m_xParaIterator = xFlatParaIterator; |
| aNewFPEntry.m_xPara = xFlatPara; |
| aNewFPEntry.m_aDocId = rDocId; |
| aNewFPEntry.m_nStartIndex = nStartIndex; |
| aNewFPEntry.m_bAutomatic = bAutomatic; |
| |
| // add new entry to the end of this queue |
| ::osl::Guard< ::osl::Mutex > aGuard( MyMutex::get() ); |
| m_aFPEntriesQueue.push_back( aNewFPEntry ); |
| |
| // wake up the thread in order to do grammar checking |
| m_aWakeUpThread.set(); |
| } |
| } |
| |
| |
| void GrammarCheckingIterator::ProcessResult( |
| const linguistic2::ProofreadingResult &rRes, |
| const uno::Reference< text::XFlatParagraphIterator > &rxFlatParagraphIterator, |
| bool bIsAutomaticChecking ) |
| { |
| DBG_ASSERT( rRes.xFlatParagraph.is(), "xFlatParagraph is missing" ); |
| //no guard necessary as no members are used |
| sal_Bool bContinueWithNextPara = sal_False; |
| if (!rRes.xFlatParagraph.is() || rRes.xFlatParagraph->isModified()) |
| { |
| // if paragraph was modified/deleted meanwhile continue with the next one... |
| bContinueWithNextPara = sal_True; |
| } |
| else // paragraph is still unchanged... |
| { |
| // |
| // mark found errors... |
| // |
| |
| sal_Int32 nTextLen = rRes.aText.getLength(); |
| bool bBoundariesOk = 0 <= rRes.nStartOfSentencePosition && rRes.nStartOfSentencePosition <= nTextLen && |
| 0 <= rRes.nBehindEndOfSentencePosition && rRes.nBehindEndOfSentencePosition <= nTextLen && |
| 0 <= rRes.nStartOfNextSentencePosition && rRes.nStartOfNextSentencePosition <= nTextLen && |
| rRes.nStartOfSentencePosition <= rRes.nBehindEndOfSentencePosition && |
| rRes.nBehindEndOfSentencePosition <= rRes.nStartOfNextSentencePosition; |
| (void) bBoundariesOk; |
| DBG_ASSERT( bBoundariesOk, "inconsistent sentence boundaries" ); |
| uno::Sequence< linguistic2::SingleProofreadingError > aErrors = rRes.aErrors; |
| |
| uno::Reference< text::XMultiTextMarkup > xMulti( rRes.xFlatParagraph, uno::UNO_QUERY ); |
| if (xMulti.is()) // use new API for markups |
| { |
| try |
| { |
| // length = number of found errors + 1 sentence markup |
| sal_Int32 nErrors = rRes.aErrors.getLength(); |
| uno::Sequence< text::TextMarkupDescriptor > aDescriptors( nErrors + 1 ); |
| text::TextMarkupDescriptor * pDescriptors = aDescriptors.getArray(); |
| |
| // at pos 0 .. nErrors-1 -> all grammar errors |
| for (sal_Int32 i = 0; i < nErrors; ++i) |
| { |
| const linguistic2::SingleProofreadingError &rError = rRes.aErrors[i]; |
| text::TextMarkupDescriptor &rDesc = aDescriptors[i]; |
| |
| rDesc.nType = rError.nErrorType; |
| rDesc.nOffset = rError.nErrorStart; |
| rDesc.nLength = rError.nErrorLength; |
| |
| // the proofreader may return SPELLING but right now our core |
| // does only handle PROOFREADING if the result is from the proofreader... |
| // (later on we may wish to color spelling errors found by the proofreader |
| // differently for example. But no special handling right now. |
| if (rDesc.nType == text::TextMarkupType::SPELLCHECK) |
| rDesc.nType = text::TextMarkupType::PROOFREADING; |
| } |
| |
| // at pos nErrors -> sentence markup |
| // nSentenceLength: includes the white-spaces following the sentence end... |
| const sal_Int32 nSentenceLength = rRes.nStartOfNextSentencePosition - rRes.nStartOfSentencePosition; |
| pDescriptors[ nErrors ].nType = text::TextMarkupType::SENTENCE; |
| pDescriptors[ nErrors ].nOffset = rRes.nStartOfSentencePosition; |
| pDescriptors[ nErrors ].nLength = nSentenceLength; |
| |
| xMulti->commitMultiTextMarkup( aDescriptors ) ; |
| } |
| catch (lang::IllegalArgumentException &) |
| { |
| DBG_ERROR( "commitMultiTextMarkup: IllegalArgumentException exception caught" ); |
| } |
| } |
| |
| // other sentences left to be checked in this paragraph? |
| if (rRes.nStartOfNextSentencePosition < rRes.aText.getLength()) |
| { |
| AddEntry( rxFlatParagraphIterator, rRes.xFlatParagraph, rRes.aDocumentIdentifier, rRes.nStartOfNextSentencePosition, bIsAutomaticChecking ); |
| } |
| else // current paragraph finished |
| { |
| // set "already checked" flag for the current flat paragraph |
| if (rRes.xFlatParagraph.is()) |
| rRes.xFlatParagraph->setChecked( text::TextMarkupType::PROOFREADING, true ); |
| |
| bContinueWithNextPara = sal_True; |
| } |
| } |
| |
| if (bContinueWithNextPara) |
| { |
| // we need to continue with the next paragraph |
| uno::Reference< text::XFlatParagraph > xFlatParaNext; |
| if (rxFlatParagraphIterator.is()) |
| xFlatParaNext = rxFlatParagraphIterator->getNextPara(); |
| { |
| AddEntry( rxFlatParagraphIterator, xFlatParaNext, rRes.aDocumentIdentifier, 0, bIsAutomaticChecking ); |
| } |
| } |
| } |
| |
| |
| uno::Reference< linguistic2::XProofreader > GrammarCheckingIterator::GetGrammarChecker( |
| const lang::Locale &rLocale ) |
| { |
| (void) rLocale; |
| uno::Reference< linguistic2::XProofreader > xRes; |
| |
| // ---- THREAD SAFE START ---- |
| ::osl::Guard< ::osl::Mutex > aGuard( MyMutex::get() ); |
| |
| // check supported locales for each grammarchecker if not already done |
| if (!m_bGCServicesChecked) |
| { |
| //GetAvailableGCSvcs_Impl(); |
| GetConfiguredGCSvcs_Impl(); |
| //GetMatchingGCSvcs_Impl(); |
| m_bGCServicesChecked = sal_True; |
| } |
| |
| const LanguageType nLang = MsLangId::convertLocaleToLanguage( rLocale ); |
| GCImplNames_t::const_iterator aLangIt( m_aGCImplNamesByLang.find( nLang ) ); |
| if (aLangIt != m_aGCImplNamesByLang.end()) // matching configured language found? |
| { |
| OUString aSvcImplName( aLangIt->second ); |
| GCReferences_t::const_iterator aImplNameIt( m_aGCReferencesByService.find( aSvcImplName ) ); |
| if (aImplNameIt != m_aGCReferencesByService.end()) // matching impl name found? |
| { |
| xRes = aImplNameIt->second; |
| } |
| else // the service is to be instatiated here for the first time... |
| { |
| try |
| { |
| uno::Reference< lang::XMultiServiceFactory > xMgr( |
| utl::getProcessServiceFactory(), uno::UNO_QUERY_THROW ); |
| uno::Reference< linguistic2::XProofreader > xGC( |
| xMgr->createInstance( aSvcImplName ), uno::UNO_QUERY_THROW ); |
| uno::Reference< linguistic2::XSupportedLocales > xSuppLoc( xGC, uno::UNO_QUERY_THROW ); |
| |
| if (xSuppLoc->hasLocale( rLocale )) |
| { |
| m_aGCReferencesByService[ aSvcImplName ] = xGC; |
| xRes = xGC; |
| |
| uno::Reference< linguistic2::XLinguServiceEventBroadcaster > xBC( xGC, uno::UNO_QUERY ); |
| if (xBC.is()) |
| xBC->addLinguServiceEventListener( this ); |
| } |
| else |
| { |
| DBG_ASSERT( 0, "grammar checker does not support required locale" ); |
| } |
| } |
| catch (uno::Exception &) |
| { |
| DBG_ASSERT( 0, "instantiating grammar checker failed" ); |
| } |
| } |
| } |
| // ---- THREAD SAFE END ---- |
| |
| return xRes; |
| } |
| |
| |
| void GrammarCheckingIterator::DequeueAndCheck() |
| { |
| uno::Sequence< sal_Int32 > aLangPortions; |
| uno::Sequence< lang::Locale > aLangPortionsLocale; |
| |
| // ---- THREAD SAFE START ---- |
| bool bEnd = false; |
| { |
| ::osl::Guard< ::osl::Mutex > aGuard( MyMutex::get() ); |
| bEnd = m_bEnd; |
| } |
| // ---- THREAD SAFE END ---- |
| while (!bEnd) |
| { |
| // ---- THREAD SAFE START ---- |
| bool bQueueEmpty = false; |
| { |
| ::osl::Guard< ::osl::Mutex > aGuard( MyMutex::get() ); |
| bQueueEmpty = m_aFPEntriesQueue.empty(); |
| } |
| // ---- THREAD SAFE END ---- |
| |
| if (!bQueueEmpty) |
| { |
| uno::Reference< text::XFlatParagraphIterator > xFPIterator; |
| uno::Reference< text::XFlatParagraph > xFlatPara; |
| FPEntry aFPEntryItem; |
| OUString aCurDocId; |
| sal_Bool bModified = sal_False; |
| // ---- THREAD SAFE START ---- |
| { |
| ::osl::Guard< ::osl::Mutex > aGuard( MyMutex::get() ); |
| aFPEntryItem = m_aFPEntriesQueue.front(); |
| xFPIterator = aFPEntryItem.m_xParaIterator; |
| xFlatPara = aFPEntryItem.m_xPara; |
| m_aCurCheckedDocId = aFPEntryItem.m_aDocId; |
| aCurDocId = m_aCurCheckedDocId; |
| |
| m_aFPEntriesQueue.pop_front(); |
| } |
| // ---- THREAD SAFE END ---- |
| |
| if (xFlatPara.is() && xFPIterator.is()) |
| { |
| OUString aCurTxt( xFlatPara->getText() ); |
| lang::Locale aCurLocale = lcl_GetPrimaryLanguageOfSentence( xFlatPara, aFPEntryItem.m_nStartIndex ); |
| |
| bModified = xFlatPara->isModified(); |
| if (!bModified) |
| { |
| // ---- THREAD SAFE START ---- |
| ::osl::ClearableGuard< ::osl::Mutex > aGuard( MyMutex::get() ); |
| |
| sal_Int32 nStartPos = aFPEntryItem.m_nStartIndex; |
| sal_Int32 nSuggestedEnd = GetSuggestedEndOfSentence( aCurTxt, nStartPos, aCurLocale ); |
| DBG_ASSERT( (nSuggestedEnd == 0 && aCurTxt.getLength() == 0) || nSuggestedEnd > nStartPos, |
| "nSuggestedEndOfSentencePos calculation failed?" ); |
| |
| linguistic2::ProofreadingResult aRes; |
| |
| uno::Reference< linguistic2::XProofreader > xGC( GetGrammarChecker( aCurLocale ), uno::UNO_QUERY ); |
| if (xGC.is()) |
| { |
| aGuard.clear(); |
| uno::Sequence< beans::PropertyValue > aEmptyProps; |
| aRes = xGC->doProofreading( aCurDocId, aCurTxt, aCurLocale, nStartPos, nSuggestedEnd, aEmptyProps ); |
| |
| //!! work-around to prevent looping if the grammar checker |
| //!! failed to properly identify the sentence end |
| if (aRes.nBehindEndOfSentencePosition <= nStartPos) |
| { |
| DBG_ASSERT( 0, "!! Grammarchecker failed to provide end of sentence !!" ); |
| aRes.nBehindEndOfSentencePosition = nSuggestedEnd; |
| } |
| |
| aRes.xFlatParagraph = xFlatPara; |
| aRes.nStartOfSentencePosition = nStartPos; |
| } |
| else |
| { |
| // no grammar checker -> no error |
| // but we need to provide the data below in order to continue with the next sentence |
| aRes.aDocumentIdentifier = aCurDocId; |
| aRes.xFlatParagraph = xFlatPara; |
| aRes.aText = aCurTxt; |
| aRes.aLocale = aCurLocale; |
| aRes.nStartOfSentencePosition = nStartPos; |
| aRes.nBehindEndOfSentencePosition = nSuggestedEnd; |
| } |
| aRes.nStartOfNextSentencePosition = lcl_SkipWhiteSpaces( aCurTxt, aRes.nBehindEndOfSentencePosition ); |
| aRes.nBehindEndOfSentencePosition = lcl_BacktraceWhiteSpaces( aCurTxt, aRes.nStartOfNextSentencePosition ); |
| |
| //guard has to be cleared as ProcessResult calls out of this class |
| aGuard.clear(); |
| ProcessResult( aRes, xFPIterator, aFPEntryItem.m_bAutomatic ); |
| // ---- THREAD SAFE END ---- |
| } |
| else |
| { |
| // the paragraph changed meanwhile... (and maybe is still edited) |
| // thus we simply continue to ask for the next to be checked. |
| uno::Reference< text::XFlatParagraph > xFlatParaNext( xFPIterator->getNextPara() ); |
| AddEntry( xFPIterator, xFlatParaNext, aCurDocId, 0, aFPEntryItem.m_bAutomatic ); |
| } |
| } |
| |
| // ---- THREAD SAFE START ---- |
| { |
| ::osl::Guard< ::osl::Mutex > aGuard( MyMutex::get() ); |
| m_aCurCheckedDocId = OUString(); |
| } |
| // ---- THREAD SAFE END ---- |
| } |
| else |
| { |
| // ---- THREAD SAFE START ---- |
| { |
| ::osl::Guard< ::osl::Mutex > aGuard( MyMutex::get() ); |
| // Check queue state again |
| if (m_aFPEntriesQueue.empty()) |
| m_aWakeUpThread.reset(); |
| } |
| // ---- THREAD SAFE END ---- |
| |
| //if the queue is empty |
| // IMPORTANT: Don't call condition.wait() with locked |
| // mutex. Otherwise you would keep out other threads |
| // to add entries to the queue! A condition is thread- |
| // safe implemented. |
| m_aWakeUpThread.wait(); |
| } |
| |
| // ---- THREAD SAFE START ---- |
| { |
| ::osl::Guard< ::osl::Mutex > aGuard( MyMutex::get() ); |
| bEnd = m_bEnd; |
| } |
| // ---- THREAD SAFE END ---- |
| } |
| |
| //!! This one must be the very last statement to call in this function !! |
| m_aRequestEndThread.set(); |
| } |
| |
| |
| void SAL_CALL GrammarCheckingIterator::startProofreading( |
| const uno::Reference< ::uno::XInterface > & xDoc, |
| const uno::Reference< text::XFlatParagraphIteratorProvider > & xIteratorProvider ) |
| throw (uno::RuntimeException, lang::IllegalArgumentException) |
| { |
| // get paragraph to start checking with |
| const bool bAutomatic = true; |
| uno::Reference<text::XFlatParagraphIterator> xFPIterator = xIteratorProvider->getFlatParagraphIterator( |
| text::TextMarkupType::PROOFREADING, bAutomatic ); |
| uno::Reference< text::XFlatParagraph > xPara( xFPIterator.is()? xFPIterator->getFirstPara() : NULL ); |
| uno::Reference< lang::XComponent > xComponent( xDoc, uno::UNO_QUERY ); |
| |
| // ---- THREAD SAFE START ---- |
| ::osl::Guard< ::osl::Mutex > aGuard( MyMutex::get() ); |
| if (xPara.is() && xComponent.is()) |
| { |
| OUString aDocId = GetOrCreateDocId( xComponent ); |
| |
| // create new entry and add it to queue |
| AddEntry( xFPIterator, xPara, aDocId, 0, bAutomatic ); |
| } |
| // ---- THREAD SAFE END ---- |
| } |
| |
| |
| linguistic2::ProofreadingResult SAL_CALL GrammarCheckingIterator::checkSentenceAtPosition( |
| const uno::Reference< uno::XInterface >& xDoc, |
| const uno::Reference< text::XFlatParagraph >& xFlatPara, |
| const OUString& rText, |
| const lang::Locale& rLocale, |
| sal_Int32 nStartOfSentencePos, |
| sal_Int32 nSuggestedEndOfSentencePos, |
| sal_Int32 nErrorPosInPara ) |
| throw (lang::IllegalArgumentException, uno::RuntimeException) |
| { |
| (void) rLocale; |
| |
| // for the context menu... |
| |
| linguistic2::ProofreadingResult aRes; |
| |
| uno::Reference< lang::XComponent > xComponent( xDoc, uno::UNO_QUERY ); |
| if (xFlatPara.is() && xComponent.is() && |
| ( nErrorPosInPara < 0 || nErrorPosInPara < rText.getLength())) |
| { |
| // iterate through paragraph until we find the sentence we are interested in |
| linguistic2::ProofreadingResult aTmpRes; |
| sal_Int32 nStartPos = nStartOfSentencePos >= 0 ? nStartOfSentencePos : 0; |
| |
| bool bFound = false; |
| do |
| { |
| lang::Locale aCurLocale = lcl_GetPrimaryLanguageOfSentence( xFlatPara, nStartPos ); |
| sal_Int32 nOldStartOfSentencePos = nStartPos; |
| uno::Reference< linguistic2::XProofreader > xGC; |
| OUString aDocId; |
| |
| // ---- THREAD SAFE START ---- |
| { |
| ::osl::ClearableGuard< ::osl::Mutex > aGuard( MyMutex::get() ); |
| aDocId = GetOrCreateDocId( xComponent ); |
| nSuggestedEndOfSentencePos = GetSuggestedEndOfSentence( rText, nStartPos, aCurLocale ); |
| DBG_ASSERT( nSuggestedEndOfSentencePos > nStartPos, "nSuggestedEndOfSentencePos calculation failed?" ); |
| |
| xGC = GetGrammarChecker( aCurLocale ); |
| } |
| // ---- THREAD SAFE START ---- |
| sal_Int32 nEndPos = -1; |
| if (xGC.is()) |
| { |
| uno::Sequence< beans::PropertyValue > aEmptyProps; |
| aTmpRes = xGC->doProofreading( aDocId, rText, aCurLocale, nStartPos, nSuggestedEndOfSentencePos, aEmptyProps ); |
| |
| //!! work-around to prevent looping if the grammar checker |
| //!! failed to properly identify the sentence end |
| if (aTmpRes.nBehindEndOfSentencePosition <= nStartPos) |
| { |
| DBG_ASSERT( 0, "!! Grammarchecker failed to provide end of sentence !!" ); |
| aTmpRes.nBehindEndOfSentencePosition = nSuggestedEndOfSentencePos; |
| } |
| |
| aTmpRes.xFlatParagraph = xFlatPara; |
| aTmpRes.nStartOfSentencePosition = nStartPos; |
| nEndPos = aTmpRes.nBehindEndOfSentencePosition; |
| |
| if ((nErrorPosInPara< 0 || nStartPos <= nErrorPosInPara) && nErrorPosInPara < nEndPos) |
| bFound = true; |
| } |
| if (nEndPos == -1) // no result from grammar checker |
| nEndPos = nSuggestedEndOfSentencePos; |
| nStartPos = lcl_SkipWhiteSpaces( rText, nEndPos ); |
| aTmpRes.nBehindEndOfSentencePosition = nEndPos; |
| aTmpRes.nStartOfNextSentencePosition = nStartPos; |
| aTmpRes.nBehindEndOfSentencePosition = lcl_BacktraceWhiteSpaces( rText, aTmpRes.nStartOfNextSentencePosition ); |
| |
| // prevent endless loop by forcefully advancing if needs be... |
| if (nStartPos <= nOldStartOfSentencePos) |
| { |
| DBG_ASSERT( 0, "end-of-sentence detection failed?" ); |
| nStartPos = nOldStartOfSentencePos + 1; |
| } |
| } |
| while (!bFound && nStartPos < rText.getLength()); |
| |
| if (bFound && !xFlatPara->isModified()) |
| aRes = aTmpRes; |
| } |
| |
| return aRes; |
| } |
| |
| |
| sal_Int32 GrammarCheckingIterator::GetSuggestedEndOfSentence( |
| const OUString &rText, |
| sal_Int32 nSentenceStartPos, |
| const lang::Locale &rLocale ) |
| { |
| // internal method; will always be called with locked mutex |
| |
| uno::Reference< i18n::XBreakIterator > xBreakIterator; |
| if (!m_xBreakIterator.is()) |
| { |
| uno::Reference< lang::XMultiServiceFactory > xMSF = ::comphelper::getProcessServiceFactory(); |
| if ( xMSF.is() ) |
| xBreakIterator = uno::Reference < i18n::XBreakIterator >( xMSF->createInstance( |
| ::rtl::OUString::createFromAscii("com.sun.star.i18n.BreakIterator") ), uno::UNO_QUERY ); |
| } |
| sal_Int32 nTextLen = rText.getLength(); |
| sal_Int32 nEndPosition = nTextLen; |
| if (m_xBreakIterator.is()) |
| { |
| sal_Int32 nTmpStartPos = nSentenceStartPos; |
| do |
| { |
| nEndPosition = nTextLen; |
| if (nTmpStartPos < nTextLen) |
| nEndPosition = m_xBreakIterator->endOfSentence( rText, nTmpStartPos, rLocale ); |
| if (nEndPosition < 0) |
| nEndPosition = nTextLen; |
| |
| ++nTmpStartPos; |
| } |
| while (nEndPosition <= nSentenceStartPos && nEndPosition < nTextLen); |
| if (nEndPosition > nTextLen) |
| nEndPosition = nTextLen; |
| } |
| return nEndPosition; |
| } |
| |
| |
| void SAL_CALL GrammarCheckingIterator::resetIgnoreRules( ) |
| throw (uno::RuntimeException) |
| { |
| GCReferences_t::iterator aIt( m_aGCReferencesByService.begin() ); |
| while (aIt != m_aGCReferencesByService.end()) |
| { |
| uno::Reference< linguistic2::XProofreader > xGC( aIt->second ); |
| if (xGC.is()) |
| xGC->resetIgnoreRules(); |
| ++aIt; |
| } |
| } |
| |
| |
| sal_Bool SAL_CALL GrammarCheckingIterator::isProofreading( |
| const uno::Reference< uno::XInterface >& xDoc ) |
| throw (uno::RuntimeException) |
| { |
| // ---- THREAD SAFE START ---- |
| ::osl::Guard< ::osl::Mutex > aGuard( MyMutex::get() ); |
| |
| sal_Bool bRes = sal_False; |
| |
| uno::Reference< lang::XComponent > xComponent( xDoc, uno::UNO_QUERY ); |
| if (xComponent.is()) |
| { |
| // if the component was already used in one of the two calls to check text |
| // i.e. in startGrammarChecking or checkGrammarAtPos it will be found in the |
| // m_aDocIdMap unless the document already disposed. |
| // If it is not found then it is not yet being checked (or requested to being checked) |
| const DocMap_t::const_iterator aIt( m_aDocIdMap.find( xComponent.get() ) ); |
| if (aIt != m_aDocIdMap.end()) |
| { |
| // check in document is checked automatically in the background... |
| OUString aDocId = aIt->second; |
| if (m_aCurCheckedDocId.getLength() > 0 && m_aCurCheckedDocId == aDocId) |
| { |
| // an entry for that document was dequed and is currently being checked. |
| bRes = sal_True; |
| } |
| else |
| { |
| // we need to check if there is an entry for that document in the queue... |
| // That is the document is going to be checked sooner or later. |
| |
| sal_Int32 nSize = m_aFPEntriesQueue.size(); |
| for (sal_Int32 i = 0; i < nSize && !bRes; ++i) |
| { |
| if (aDocId == m_aFPEntriesQueue[i].m_aDocId) |
| bRes = sal_True; |
| } |
| } |
| } |
| } |
| // ---- THREAD SAFE END ---- |
| |
| return bRes; |
| } |
| |
| |
| void SAL_CALL GrammarCheckingIterator::processLinguServiceEvent( |
| const linguistic2::LinguServiceEvent& rLngSvcEvent ) |
| throw (uno::RuntimeException) |
| { |
| if (rLngSvcEvent.nEvent == linguistic2::LinguServiceEventFlags::PROOFREAD_AGAIN) |
| { |
| try |
| { |
| uno::Reference< uno::XInterface > xThis( dynamic_cast< XLinguServiceEventBroadcaster * >(this) ); |
| linguistic2::LinguServiceEvent aEvent( xThis, linguistic2::LinguServiceEventFlags::PROOFREAD_AGAIN ); |
| m_aNotifyListeners.notifyEach( |
| &linguistic2::XLinguServiceEventListener::processLinguServiceEvent, |
| aEvent); |
| } |
| catch (uno::RuntimeException &) |
| { |
| throw; |
| } |
| catch (::uno::Exception &rE) |
| { |
| (void) rE; |
| // ignore |
| DBG_WARNING1("processLinguServiceEvent: exception:\n%s", |
| OUStringToOString(rE.Message, RTL_TEXTENCODING_UTF8).getStr()); |
| } |
| } |
| } |
| |
| |
| sal_Bool SAL_CALL GrammarCheckingIterator::addLinguServiceEventListener( |
| const uno::Reference< linguistic2::XLinguServiceEventListener >& xListener ) |
| throw (uno::RuntimeException) |
| { |
| if (xListener.is()) |
| { |
| // ::osl::Guard< ::osl::Mutex > aGuard( MyMutex::get() ); |
| m_aNotifyListeners.addInterface( xListener ); |
| } |
| return sal_True; |
| } |
| |
| |
| sal_Bool SAL_CALL GrammarCheckingIterator::removeLinguServiceEventListener( |
| const uno::Reference< linguistic2::XLinguServiceEventListener >& xListener ) |
| throw (uno::RuntimeException) |
| { |
| if (xListener.is()) |
| { |
| // ::osl::Guard< ::osl::Mutex > aGuard( MyMutex::get() ); |
| m_aNotifyListeners.removeInterface( xListener ); |
| } |
| return sal_True; |
| } |
| |
| |
| void SAL_CALL GrammarCheckingIterator::dispose() |
| throw (uno::RuntimeException) |
| { |
| lang::EventObject aEvt( (linguistic2::XProofreadingIterator *) this ); |
| m_aEventListeners.disposeAndClear( aEvt ); |
| |
| // |
| // now end the thread... |
| // |
| m_aRequestEndThread.reset(); |
| // ---- THREAD SAFE START ---- |
| { |
| ::osl::Guard< ::osl::Mutex > aGuard( MyMutex::get() ); |
| m_bEnd = sal_True; |
| } |
| // ---- THREAD SAFE END ---- |
| m_aWakeUpThread.set(); |
| const TimeValue aTime = { 3, 0 }; // wait 3 seconds... |
| m_aRequestEndThread.wait( &aTime ); |
| // if the call ends because of time-out we will end anyway... |
| |
| |
| // ---- THREAD SAFE START ---- |
| { |
| ::osl::Guard< ::osl::Mutex > aGuard( MyMutex::get() ); |
| |
| // releaase all UNO references |
| |
| m_xMSF.clear(); |
| m_xBreakIterator.clear(); |
| |
| // clear containers with UNO references AND have those references released |
| GCReferences_t aTmpEmpty1; |
| DocMap_t aTmpEmpty2; |
| FPQueue_t aTmpEmpty3; |
| m_aGCReferencesByService.swap( aTmpEmpty1 ); |
| m_aDocIdMap.swap( aTmpEmpty2 ); |
| m_aFPEntriesQueue.swap( aTmpEmpty3 ); |
| } |
| // ---- THREAD SAFE END ---- |
| } |
| |
| |
| void SAL_CALL GrammarCheckingIterator::addEventListener( |
| const uno::Reference< lang::XEventListener >& xListener ) |
| throw (uno::RuntimeException) |
| { |
| if (xListener.is()) |
| { |
| // ::osl::Guard< ::osl::Mutex > aGuard( MyMutex::get() ); |
| m_aEventListeners.addInterface( xListener ); |
| } |
| } |
| |
| |
| void SAL_CALL GrammarCheckingIterator::removeEventListener( |
| const uno::Reference< lang::XEventListener >& xListener ) |
| throw (uno::RuntimeException) |
| { |
| if (xListener.is()) |
| { |
| // ::osl::Guard< ::osl::Mutex > aGuard( MyMutex::get() ); |
| m_aEventListeners.removeInterface( xListener ); |
| } |
| } |
| |
| |
| void SAL_CALL GrammarCheckingIterator::disposing( const lang::EventObject &rSource ) |
| throw (uno::RuntimeException) |
| { |
| // if the component (document) is disposing release all references |
| //!! There is no need to remove entries from the queue that are from this document |
| //!! since the respectives xFlatParagraphs should become invalid (isModified() == true) |
| //!! and the call to xFlatParagraphIterator->getNextPara() will result in an empty reference. |
| //!! And if an entry is currently checked by a grammar checker upon return the results |
| //!! should be ignored. |
| //!! Also GetOrCreateDocId will not use that very same Id again... |
| //!! All of the above resulting in that we only have to get rid of the implementation pointer here. |
| uno::Reference< lang::XComponent > xDoc( rSource.Source, uno::UNO_QUERY ); |
| if (xDoc.is()) |
| { |
| // ---- THREAD SAFE START ---- |
| ::osl::Guard< ::osl::Mutex > aGuard( MyMutex::get() ); |
| m_aDocIdMap.erase( xDoc.get() ); |
| // ---- THREAD SAFE END ---- |
| } |
| } |
| |
| |
| uno::Reference< util::XChangesBatch > GrammarCheckingIterator::GetUpdateAccess() const |
| { |
| if (!m_xUpdateAccess.is()) |
| { |
| try |
| { |
| // get configuration provider |
| uno::Reference< lang::XMultiServiceFactory > xConfigurationProvider; |
| uno::Reference< lang::XMultiServiceFactory > xMgr = utl::getProcessServiceFactory(); |
| if (xMgr.is()) |
| { |
| xConfigurationProvider = uno::Reference< lang::XMultiServiceFactory > ( |
| xMgr->createInstance( OUString( RTL_CONSTASCII_USTRINGPARAM( |
| "com.sun.star.configuration.ConfigurationProvider" ) ) ), |
| uno::UNO_QUERY_THROW ) ; |
| } |
| |
| // get configuration update access |
| beans::PropertyValue aValue; |
| aValue.Name = A2OU( "nodepath" ); |
| aValue.Value = uno::makeAny( A2OU("org.openoffice.Office.Linguistic/ServiceManager") ); |
| uno::Sequence< uno::Any > aProps(1); |
| aProps[0] <<= aValue; |
| m_xUpdateAccess = uno::Reference< util::XChangesBatch >( |
| xConfigurationProvider->createInstanceWithArguments( |
| A2OU( "com.sun.star.configuration.ConfigurationUpdateAccess" ), aProps ), |
| uno::UNO_QUERY_THROW ); |
| } |
| catch (uno::Exception &) |
| { |
| } |
| } |
| |
| return m_xUpdateAccess; |
| } |
| |
| |
| void GrammarCheckingIterator::GetConfiguredGCSvcs_Impl() |
| { |
| GCImplNames_t aTmpGCImplNamesByLang; |
| |
| try |
| { |
| // get node names (locale iso strings) for configured grammar checkers |
| uno::Reference< container::XNameAccess > xNA( GetUpdateAccess(), uno::UNO_QUERY_THROW ); |
| xNA.set( xNA->getByName( A2OU("GrammarCheckerList") ), uno::UNO_QUERY_THROW ); |
| const uno::Sequence< OUString > aElementNames( xNA->getElementNames() ); |
| const OUString *pElementNames = aElementNames.getConstArray(); |
| |
| sal_Int32 nLen = aElementNames.getLength(); |
| for (sal_Int32 i = 0; i < nLen; ++i) |
| { |
| uno::Sequence< OUString > aImplNames; |
| uno::Any aTmp( xNA->getByName( pElementNames[i] ) ); |
| if (aTmp >>= aImplNames) |
| { |
| if (aImplNames.getLength() > 0) |
| { |
| // only the first entry is used, there should be only one grammar checker per language |
| const OUString aImplName( aImplNames[0] ); |
| const LanguageType nLang = MsLangId::convertIsoStringToLanguage( pElementNames[i] ); |
| aTmpGCImplNamesByLang[ nLang ] = aImplName; |
| } |
| } |
| else |
| { |
| DBG_ASSERT( 0, "failed to get aImplNames. Wrong type?" ); |
| } |
| } |
| } |
| catch (uno::Exception &) |
| { |
| DBG_ASSERT( 0, "exception caught. Failed to get configured services" ); |
| } |
| |
| { |
| // ---- THREAD SAFE START ---- |
| ::osl::Guard< ::osl::Mutex > aGuard( MyMutex::get() ); |
| m_aGCImplNamesByLang = aTmpGCImplNamesByLang; |
| // ---- THREAD SAFE END ---- |
| } |
| } |
| |
| /* |
| void GrammarCheckingIterator::GetMatchingGCSvcs_Impl() |
| { |
| GCImplNames_t aTmpGCImplNamesByLang; |
| |
| try |
| { |
| // get node names (locale iso strings) for configured grammar checkers |
| uno::Reference< container::XNameAccess > xNA( GetUpdateAccess(), uno::UNO_QUERY_THROW ); |
| xNA.set( xNA->getByName( A2OU("GrammarCheckers") ), uno::UNO_QUERY_THROW ); |
| const uno::Sequence< OUString > aGCImplNames( xNA->getElementNames() ); |
| const OUString *pGCImplNames = aGCImplNames.getConstArray(); |
| |
| sal_Int32 nLen = aGCImplNames.getLength(); |
| for (sal_Int32 i = 0; i < nLen; ++i) |
| { |
| uno::Reference< container::XNameAccess > xTmpNA( xNA->getByName( pGCImplNames[i] ), uno::UNO_QUERY_THROW ); |
| uno::Any aTmp( xTmpNA->getByName( A2OU("Locales") ) ); |
| uno::Sequence< OUString > aIsoLocaleNames; |
| if (aTmp >>= aIsoLocaleNames) |
| { |
| const OUString *pIsoLocaleNames = aIsoLocaleNames.getConstArray(); |
| for (sal_Int32 k = 0; k < aIsoLocaleNames.getLength(); ++k) |
| { |
| // if there are more grammar checkers for one language, for the time being, |
| // the last one found here will win... |
| const LanguageType nLang = MsLangId::convertIsoStringToLanguage( pIsoLocaleNames[k] ); |
| aTmpGCImplNamesByLang[ nLang ] = pGCImplNames[i]; |
| } |
| } |
| else |
| { |
| DBG_ASSERT( 0, "failed to get aImplNames. Wrong type?" ); |
| } |
| } |
| } |
| catch (uno::Exception &) |
| { |
| DBG_ASSERT( 0, "exception caught. Failed to get matching grammar checker services" ); |
| } |
| |
| { |
| // ---- THREAD SAFE START ---- |
| ::osl::Guard< ::osl::Mutex > aGuard( MyMutex::get() ); |
| m_aGCImplNamesByLang = aTmpGCImplNamesByLang; |
| // ---- THREAD SAFE END ---- |
| } |
| } |
| */ |
| |
| /* |
| void GrammarCheckingIterator::GetAvailableGCSvcs_Impl() |
| { |
| // internal method; will always be called with locked mutex |
| if (m_xMSF.is()) |
| { |
| uno::Reference< container::XContentEnumerationAccess > xEnumAccess( m_xMSF, uno::UNO_QUERY ); |
| uno::Reference< container::XEnumeration > xEnum; |
| if (xEnumAccess.is()) |
| xEnum = xEnumAccess->createContentEnumeration( A2OU( SN_GRAMMARCHECKER ) ); |
| |
| if (xEnum.is()) |
| { |
| while (xEnum->hasMoreElements()) |
| { |
| uno::Any aCurrent = xEnum->nextElement(); |
| uno::Reference< lang::XSingleComponentFactory > xCompFactory; |
| uno::Reference< lang::XSingleServiceFactory > xFactory; |
| |
| uno::Reference< uno::XComponentContext > xContext; |
| uno::Reference< beans::XPropertySet > xProps( m_xMSF, uno::UNO_QUERY ); |
| xProps->getPropertyValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "DefaultContext" ))) >>= xContext; |
| |
| if ( xContext.is() && |
| (cppu::extractInterface( xCompFactory, aCurrent ) || |
| cppu::extractInterface( xFactory, aCurrent )) ) |
| { |
| try |
| { |
| uno::Reference< linguistic2::XProofreader > xSvc( ( xCompFactory.is() ? xCompFactory->createInstanceWithContext( xContext ) : xFactory->createInstance() ), uno::UNO_QUERY ); |
| if (xSvc.is()) |
| { |
| OUString aImplName; |
| uno::Reference< XServiceInfo > xInfo( xSvc, uno::UNO_QUERY ); |
| if (xInfo.is()) |
| aImplName = xInfo->getImplementationName(); |
| DBG_ASSERT( aImplName.getLength(), "empty implementation name" ); |
| uno::Reference< linguistic2::XSupportedLocales > xSuppLoc( xSvc, uno::UNO_QUERY ); |
| DBG_ASSERT( xSuppLoc.is(), "interfaces not supported" ); |
| if (xSuppLoc.is() && aImplName.getLength() > 0) |
| { |
| uno::Sequence< lang::Locale > aLocaleSequence( xSuppLoc->getLocales() ); |
| // ---- THREAD SAFE START ---- |
| ::osl::Guard< ::osl::Mutex > aGuard( MyMutex::get() ); |
| m_aGCLocalesByService[ aImplName ] = aLocaleSequence; |
| m_aGCReferencesByService[ aImplName ] = xSvc; |
| // ---- THREAD SAFE END ---- |
| } |
| } |
| } |
| catch (uno::Exception &) |
| { |
| DBG_ASSERT( 0, "instantiating grammar checker failed" ); |
| } |
| } |
| } |
| } |
| } |
| } |
| */ |
| |
| |
| sal_Bool SAL_CALL GrammarCheckingIterator::supportsService( |
| const OUString & rServiceName ) |
| throw(uno::RuntimeException) |
| { |
| uno::Sequence< OUString > aSNL = getSupportedServiceNames(); |
| const OUString * pArray = aSNL.getConstArray(); |
| for( sal_Int32 i = 0; i < aSNL.getLength(); ++i ) |
| if( pArray[i] == rServiceName ) |
| return sal_True; |
| return sal_False; |
| } |
| |
| |
| OUString SAL_CALL GrammarCheckingIterator::getImplementationName( ) throw (uno::RuntimeException) |
| { |
| return GrammarCheckingIterator_getImplementationName(); |
| } |
| |
| |
| uno::Sequence< OUString > SAL_CALL GrammarCheckingIterator::getSupportedServiceNames( ) throw (uno::RuntimeException) |
| { |
| return GrammarCheckingIterator_getSupportedServiceNames(); |
| } |
| |
| |
| void GrammarCheckingIterator::SetServiceList( |
| const lang::Locale &rLocale, |
| const uno::Sequence< OUString > &rSvcImplNames ) |
| { |
| ::osl::Guard< ::osl::Mutex > aGuard( MyMutex::get() ); |
| |
| LanguageType nLanguage = LocaleToLanguage( rLocale ); |
| OUString aImplName; |
| if (rSvcImplNames.getLength() > 0) |
| aImplName = rSvcImplNames[0]; // there is only one grammar checker per language |
| |
| if (nLanguage != LANGUAGE_NONE && nLanguage != LANGUAGE_DONTKNOW) |
| { |
| if (aImplName.getLength() > 0) |
| m_aGCImplNamesByLang[ nLanguage ] = aImplName; |
| else |
| m_aGCImplNamesByLang.erase( nLanguage ); |
| } |
| } |
| |
| |
| uno::Sequence< OUString > GrammarCheckingIterator::GetServiceList( |
| const lang::Locale &rLocale ) const |
| { |
| ::osl::Guard< ::osl::Mutex > aGuard( MyMutex::get() ); |
| |
| uno::Sequence< OUString > aRes(1); |
| |
| OUString aImplName; // there is only one grammar checker per language |
| LanguageType nLang = LocaleToLanguage( rLocale ); |
| GCImplNames_t::const_iterator aIt( m_aGCImplNamesByLang.find( nLang ) ); |
| if (aIt != m_aGCImplNamesByLang.end()) |
| aImplName = aIt->second; |
| |
| if (aImplName.getLength() > 0) |
| aRes[0] = aImplName; |
| else |
| aRes.realloc(0); |
| |
| return aRes; |
| } |
| |
| |
| LinguDispatcher::DspType GrammarCheckingIterator::GetDspType() const |
| { |
| return DSP_GRAMMAR; |
| } |
| |
| |
| /////////////////////////////////////////////////////////////////////////// |
| |
| |
| static OUString GrammarCheckingIterator_getImplementationName() throw() |
| { |
| return A2OU( "com.sun.star.lingu2.ProofreadingIterator" ); |
| } |
| |
| |
| static uno::Sequence< OUString > GrammarCheckingIterator_getSupportedServiceNames() throw() |
| { |
| uno::Sequence< OUString > aSNS( 1 ); |
| aSNS.getArray()[0] = A2OU( SN_GRAMMARCHECKINGITERATOR ); |
| return aSNS; |
| } |
| |
| |
| static uno::Reference< uno::XInterface > SAL_CALL GrammarCheckingIterator_createInstance( |
| const uno::Reference< lang::XMultiServiceFactory > & rxSMgr ) |
| throw(uno::Exception) |
| { |
| return static_cast< ::cppu::OWeakObject * >(new GrammarCheckingIterator( rxSMgr )); |
| } |
| |
| |
| void * SAL_CALL GrammarCheckingIterator_getFactory( |
| const sal_Char *pImplName, |
| lang::XMultiServiceFactory *pServiceManager, |
| void * /*pRegistryKey*/ ) |
| { |
| void * pRet = 0; |
| if ( !GrammarCheckingIterator_getImplementationName().compareToAscii( pImplName ) ) |
| { |
| uno::Reference< lang::XSingleServiceFactory > xFactory = |
| cppu::createOneInstanceFactory( |
| pServiceManager, |
| GrammarCheckingIterator_getImplementationName(), |
| GrammarCheckingIterator_createInstance, |
| GrammarCheckingIterator_getSupportedServiceNames()); |
| // acquire, because we return an interface pointer instead of a reference |
| xFactory->acquire(); |
| pRet = xFactory.get(); |
| } |
| return pRet; |
| } |