diff options
Diffstat (limited to 'linguistic/source')
34 files changed, 14766 insertions, 0 deletions
diff --git a/linguistic/source/convdic.cxx b/linguistic/source/convdic.cxx new file mode 100644 index 000000000000..c1b59ed974be --- /dev/null +++ b/linguistic/source/convdic.cxx @@ -0,0 +1,734 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_linguistic.hxx" + +#include <cppuhelper/factory.hxx> +#include <i18npool/lang.h> +#include <osl/mutex.hxx> +#include <tools/debug.hxx> +#include <tools/fsys.hxx> +#include <tools/stream.hxx> +#include <tools/stream.hxx> +#include <tools/string.hxx> +#include <tools/urlobj.hxx> +#include <ucbhelper/content.hxx> +#include <unotools/processfactory.hxx> +#include <unotools/streamwrap.hxx> +#include <unotools/ucbstreamhelper.hxx> + +#include <com/sun/star/linguistic2/XConversionDictionary.hpp> +#include <com/sun/star/linguistic2/ConversionDictionaryType.hpp> +#include <com/sun/star/linguistic2/XConversionPropertyType.hpp> +#include <com/sun/star/linguistic2/ConversionPropertyType.hpp> +#include <com/sun/star/util/XFlushable.hpp> +#include <com/sun/star/lang/Locale.hpp> +#include <com/sun/star/lang/EventObject.hpp> +#include <com/sun/star/ucb/XSimpleFileAccess.hpp> +#include <com/sun/star/uno/Reference.h> +#include <com/sun/star/registry/XRegistryKey.hpp> +#include <com/sun/star/util/XFlushListener.hpp> +#include <com/sun/star/io/XActiveDataSource.hpp> +#include <com/sun/star/io/XActiveDataSource.hpp> +#include <com/sun/star/io/XInputStream.hpp> +#include <com/sun/star/io/XOutputStream.hpp> +#include <com/sun/star/xml/sax/XDocumentHandler.hpp> +#include <com/sun/star/document/XFilter.hpp> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/xml/sax/InputSource.hpp> +#include <com/sun/star/xml/sax/XParser.hpp> + + +#include "convdic.hxx" +#include "convdicxml.hxx" +#include "linguistic/misc.hxx" +#include "defs.hxx" + +using namespace std; +using namespace utl; +using namespace osl; +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; + +using ::rtl::OUString; + +#define SN_CONV_DICTIONARY "com.sun.star.linguistic2.ConversionDictionary" +#define SN_HCD_CONV_DICTIONARY "com.sun.star.linguistic2.HangulHanjaConversionDictionary" + + +void ReadThroughDic( const String &rMainURL, ConvDicXMLImport &rImport ) +{ + if (rMainURL.Len() == 0) + return; + DBG_ASSERT(!INetURLObject( rMainURL ).HasError(), "invalid URL"); + + uno::Reference< lang::XMultiServiceFactory > xServiceFactory( utl::getProcessServiceFactory() ); + + // get xInputStream stream + uno::Reference< io::XInputStream > xIn; + try + { + uno::Reference< ucb::XSimpleFileAccess > xAccess( xServiceFactory->createInstance( + A2OU( "com.sun.star.ucb.SimpleFileAccess" ) ), uno::UNO_QUERY_THROW ); + xIn = xAccess->openFileRead( rMainURL ); + } + catch (uno::Exception & e) + { + DBG_ASSERT( 0, "failed to get input stream" ); + (void) e; + } + if (!xIn.is()) + return; + + SvStreamPtr pStream = SvStreamPtr( utl::UcbStreamHelper::CreateStream( xIn ) ); + + // prepare ParserInputSource + xml::sax::InputSource aParserInput; + aParserInput.aInputStream = xIn; + + // get parser + uno::Reference< xml::sax::XParser > xParser; + try + { + xParser = uno::Reference< xml::sax::XParser >( xServiceFactory->createInstance( + A2OU( "com.sun.star.xml.sax.Parser" ) ), UNO_QUERY ); + } + catch (uno::Exception &) + { + } + DBG_ASSERT( xParser.is(), "Can't create parser" ); + if (!xParser.is()) + return; + + //!! keep a reference until everything is done to + //!! ensure the proper lifetime of the object + uno::Reference < xml::sax::XDocumentHandler > xFilter( + (xml::sax::XExtendedDocumentHandler *) &rImport, UNO_QUERY ); + + // connect parser and filter + xParser->setDocumentHandler( xFilter ); + + // finally, parser the stream + try + { + xParser->parseStream( aParserInput ); // implicitly calls ConvDicXMLImport::CreateContext + } + catch( xml::sax::SAXParseException& ) + { + } + catch( xml::sax::SAXException& ) + { + } + catch( io::IOException& ) + { + } +} + +sal_Bool IsConvDic( const String &rFileURL, sal_Int16 &nLang, sal_Int16 &nConvType ) +{ + sal_Bool bRes = sal_False; + + if (rFileURL.Len() == 0) + return bRes; + + // check if file extension matches CONV_DIC_EXT + String aExt; + xub_StrLen nPos = rFileURL.SearchBackward( '.' ); + if (STRING_NOTFOUND != nPos) + aExt = rFileURL.Copy( nPos + 1 ); + aExt.ToLowerAscii(); + if (!aExt.EqualsAscii( CONV_DIC_EXT )) + return bRes; + + // first argument being 0 should stop the file from being parsed + // up to the end (reading all entries) when the required + // data (language, conversion type) is found. + ConvDicXMLImport *pImport = new ConvDicXMLImport( 0, rFileURL ); + + //!! keep a first reference to ensure the lifetime of the object !! + uno::Reference< XInterface > xRef( (document::XFilter *) pImport, UNO_QUERY ); + + ReadThroughDic( rFileURL, *pImport ); // will implicitly add the entries + bRes = pImport->GetLanguage() != LANGUAGE_NONE && + pImport->GetConversionType() != -1; + DBG_ASSERT( bRes, "conversion dictionary corrupted?" ); + + if (bRes) + { + nLang = pImport->GetLanguage(); + nConvType = pImport->GetConversionType(); + } + + return bRes; +} + + + +ConvDic::ConvDic( + const String &rName, + sal_Int16 nLang, + sal_Int16 nConvType, + sal_Bool bBiDirectional, + const String &rMainURL) : + aFlushListeners( GetLinguMutex() ) +{ + aName = rName; + nLanguage = nLang; + nConversionType = nConvType; + aMainURL = rMainURL; + + if (bBiDirectional) + pFromRight = std::auto_ptr< ConvMap >( new ConvMap ); + if (nLang == LANGUAGE_CHINESE_SIMPLIFIED || nLang == LANGUAGE_CHINESE_TRADITIONAL) + pConvPropType = std::auto_ptr< PropTypeMap >( new PropTypeMap ); + + nMaxLeftCharCount = nMaxRightCharCount = 0; + bMaxCharCountIsValid = sal_True; + + bNeedEntries = sal_True; + bIsModified = bIsActive = sal_False; + bIsReadonly = sal_False; + + if( rMainURL.Len() > 0 ) + { + sal_Bool bExists = sal_False; + bIsReadonly = IsReadOnly( rMainURL, &bExists ); + + if( !bExists ) // new empty dictionary + { + bNeedEntries = sal_False; + //! create physical representation of an **empty** dictionary + //! that could be found by the dictionary-list implementation + // (Note: empty dictionaries are not just empty files!) + Save(); + bIsReadonly = IsReadOnly( rMainURL ); // will be sal_False if Save was succesfull + } + } + else + { + bNeedEntries = sal_False; + } +} + + +ConvDic::~ConvDic() +{ +} + + +void ConvDic::Load() +{ + DBG_ASSERT( !bIsModified, "dictionary is modified. Really do 'Load'?" ); + + //!! prevent function from being called recursively via HasEntry, AddEntry + bNeedEntries = sal_False; + ConvDicXMLImport *pImport = new ConvDicXMLImport( this, aMainURL ); + //!! keep a first reference to ensure the lifetime of the object !! + uno::Reference< XInterface > xRef( (document::XFilter *) pImport, UNO_QUERY ); + ReadThroughDic( aMainURL, *pImport ); // will implicitly add the entries + bIsModified = sal_False; +} + + +void ConvDic::Save() +{ + DBG_ASSERT( !bNeedEntries, "saving while entries missing" ); + if (aMainURL.Len() == 0 || bNeedEntries) + return; + DBG_ASSERT(!INetURLObject( aMainURL ).HasError(), "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( aMainURL ); + } + catch (uno::Exception & e) + { + DBG_ASSERT( 0, "failed to get input stream" ); + (void) e; + } + if (!xStream.is()) + return; + + SvStreamPtr pStream = SvStreamPtr( utl::UcbStreamHelper::CreateStream( xStream ) ); + + // get XML writer + uno::Reference< io::XActiveDataSource > xSaxWriter; + if (xServiceFactory.is()) + { + try + { + xSaxWriter = uno::Reference< io::XActiveDataSource >( + xServiceFactory->createInstance( + OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.xml.sax.Writer")) ), UNO_QUERY ); + } + catch (uno::Exception &) + { + } + } + DBG_ASSERT( xSaxWriter.is(), "can't instantiate XML writer" ); + + if (xSaxWriter.is() && xStream.is()) + { + // connect XML writer to output stream + xSaxWriter->setOutputStream( xStream->getOutputStream() ); + + // prepare arguments (prepend doc handler to given arguments) + uno::Reference< xml::sax::XDocumentHandler > xDocHandler( xSaxWriter, UNO_QUERY ); + ConvDicXMLExport *pExport = new ConvDicXMLExport( *this, aMainURL, xDocHandler ); + //!! keep a first(!) reference until everything is done to + //!! ensure the proper lifetime of the object + uno::Reference< document::XFilter > aRef( (document::XFilter *) pExport ); + sal_Bool bRet = pExport->Export(); // write entries to file + DBG_ASSERT( !pStream->GetError(), "I/O error while writing to stream" ); + if (bRet) + bIsModified = sal_False; + } + DBG_ASSERT( !bIsModified, "dictionary still modified after save. Save failed?" ); +} + + +ConvMap::iterator ConvDic::GetEntry( ConvMap &rMap, const rtl::OUString &rFirstText, const rtl::OUString &rSecondText ) +{ + pair< ConvMap::iterator, ConvMap::iterator > aRange = + rMap.equal_range( rFirstText ); + ConvMap::iterator aPos = rMap.end(); + for (ConvMap::iterator aIt = aRange.first; + aIt != aRange.second && aPos == rMap.end(); + ++aIt) + { + if ((*aIt).second == rSecondText) + aPos = aIt; + } + return aPos; +} + + +sal_Bool ConvDic::HasEntry( const OUString &rLeftText, const OUString &rRightText ) +{ + if (bNeedEntries) + Load(); + ConvMap::iterator aIt = GetEntry( aFromLeft, rLeftText, rRightText ); + return aIt != aFromLeft.end(); +} + + +void ConvDic::AddEntry( const OUString &rLeftText, const OUString &rRightText ) +{ + if (bNeedEntries) + Load(); + + DBG_ASSERT(!HasEntry( rLeftText, rRightText), "entry already exists" ); + aFromLeft .insert( ConvMap::value_type( rLeftText, rRightText ) ); + if (pFromRight.get()) + pFromRight->insert( ConvMap::value_type( rRightText, rLeftText ) ); + + if (bMaxCharCountIsValid) + { + if (rLeftText.getLength() > nMaxLeftCharCount) + nMaxLeftCharCount = (sal_Int16) rLeftText.getLength(); + if (pFromRight.get() && rRightText.getLength() > nMaxRightCharCount) + nMaxRightCharCount = (sal_Int16) rRightText.getLength(); + } + + bIsModified = sal_True; +} + + +void ConvDic::RemoveEntry( const OUString &rLeftText, const OUString &rRightText ) +{ + if (bNeedEntries) + Load(); + + ConvMap::iterator aLeftIt = GetEntry( aFromLeft, rLeftText, rRightText ); + DBG_ASSERT( aLeftIt != aFromLeft.end(), "left map entry missing" ); + aFromLeft .erase( aLeftIt ); + + if (pFromRight.get()) + { + ConvMap::iterator aRightIt = GetEntry( *pFromRight, rRightText, rLeftText ); + DBG_ASSERT( aRightIt != pFromRight->end(), "right map entry missing" ); + pFromRight->erase( aRightIt ); + } + + bIsModified = sal_True; + bMaxCharCountIsValid = sal_False; +} + + +OUString SAL_CALL ConvDic::getName( ) + throw (RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + return aName; +} + + +Locale SAL_CALL ConvDic::getLocale( ) + throw (RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + return CreateLocale( nLanguage ); +} + + +sal_Int16 SAL_CALL ConvDic::getConversionType( ) + throw (RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + return nConversionType; +} + + +void SAL_CALL ConvDic::setActive( sal_Bool bActivate ) + throw (RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + bIsActive = bActivate; +} + + +sal_Bool SAL_CALL ConvDic::isActive( ) + throw (RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + return bIsActive; +} + + +void SAL_CALL ConvDic::clear( ) + throw (RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + aFromLeft .clear(); + if (pFromRight.get()) + pFromRight->clear(); + bNeedEntries = sal_False; + bIsModified = sal_True; + nMaxLeftCharCount = 0; + nMaxRightCharCount = 0; + bMaxCharCountIsValid = sal_True; +} + + +uno::Sequence< OUString > SAL_CALL ConvDic::getConversions( + const OUString& aText, + sal_Int32 nStartPos, + sal_Int32 nLength, + ConversionDirection eDirection, + sal_Int32 /*nTextConversionOptions*/ ) + throw (IllegalArgumentException, RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + + if (!pFromRight.get() && eDirection == ConversionDirection_FROM_RIGHT) + return uno::Sequence< OUString >(); + + if (bNeedEntries) + Load(); + + OUString aLookUpText( aText.copy(nStartPos, nLength) ); + ConvMap &rConvMap = eDirection == ConversionDirection_FROM_LEFT ? + aFromLeft : *pFromRight; + pair< ConvMap::iterator, ConvMap::iterator > aRange = + rConvMap.equal_range( aLookUpText ); + + sal_Int32 nCount = 0; + ConvMap::iterator aIt; + for (aIt = aRange.first; aIt != aRange.second; ++aIt) + ++nCount; + + uno::Sequence< OUString > aRes( nCount ); + OUString *pRes = aRes.getArray(); + sal_Int32 i = 0; + for (aIt = aRange.first; aIt != aRange.second; ++aIt) + pRes[i++] = (*aIt).second; + + return aRes; +} + + +static sal_Bool lcl_SeqHasEntry( + const OUString *pSeqStart, // first element to check + sal_Int32 nToCheck, // number of elements to check + const OUString &rText) +{ + sal_Bool bRes = sal_False; + if (pSeqStart && nToCheck > 0) + { + const OUString *pDone = pSeqStart + nToCheck; // one behind last to check + while (!bRes && pSeqStart != pDone) + { + if (*pSeqStart++ == rText) + bRes = sal_True; + } + } + return bRes; +} + +uno::Sequence< OUString > SAL_CALL ConvDic::getConversionEntries( + ConversionDirection eDirection ) + throw (RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + + if (!pFromRight.get() && eDirection == ConversionDirection_FROM_RIGHT) + return uno::Sequence< OUString >(); + + if (bNeedEntries) + Load(); + + ConvMap &rConvMap = eDirection == ConversionDirection_FROM_LEFT ? + aFromLeft : *pFromRight; + uno::Sequence< OUString > aRes( rConvMap.size() ); + OUString *pRes = aRes.getArray(); + ConvMap::iterator aIt = rConvMap.begin(); + sal_Int32 nIdx = 0; + while (aIt != rConvMap.end()) + { + OUString aCurEntry( (*aIt).first ); + // skip duplicate entries ( duplicate = duplicate entries + // respective to the evaluated side (FROM_LEFT or FROM_RIGHT). + // Thus if FROM_LEFT is evaluated for pairs (A,B) and (A,C) + // only one entry for A will be returned in the result) + if (nIdx == 0 || !lcl_SeqHasEntry( pRes, nIdx, aCurEntry )) + pRes[ nIdx++ ] = aCurEntry; + ++aIt; + } + aRes.realloc( nIdx ); + + return aRes; +} + + +void SAL_CALL ConvDic::addEntry( + const OUString& aLeftText, + const OUString& aRightText ) + throw (IllegalArgumentException, container::ElementExistException, RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + if (bNeedEntries) + Load(); + if (HasEntry( aLeftText, aRightText )) + throw container::ElementExistException(); + AddEntry( aLeftText, aRightText ); +} + + +void SAL_CALL ConvDic::removeEntry( + const OUString& aLeftText, + const OUString& aRightText ) + throw (container::NoSuchElementException, RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + if (bNeedEntries) + Load(); + if (!HasEntry( aLeftText, aRightText )) + throw container::NoSuchElementException(); + RemoveEntry( aLeftText, aRightText ); +} + + +sal_Int16 SAL_CALL ConvDic::getMaxCharCount( ConversionDirection eDirection ) + throw (RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + + if (!pFromRight.get() && eDirection == ConversionDirection_FROM_RIGHT) + { + DBG_ASSERT( nMaxRightCharCount == 0, "max right char count should be 0" ); + return 0; + } + + if (bNeedEntries) + Load(); + + if (!bMaxCharCountIsValid) + { + nMaxLeftCharCount = 0; + ConvMap::iterator aIt = aFromLeft.begin(); + while (aIt != aFromLeft.end()) + { + sal_Int16 nTmp = (sal_Int16) (*aIt).first.getLength(); + if (nTmp > nMaxLeftCharCount) + nMaxLeftCharCount = nTmp; + ++aIt; + } + + nMaxRightCharCount = 0; + if (pFromRight.get()) + { + aIt = pFromRight->begin(); + while (aIt != pFromRight->end()) + { + sal_Int16 nTmp = (sal_Int16) (*aIt).first.getLength(); + if (nTmp > nMaxRightCharCount) + nMaxRightCharCount = nTmp; + ++aIt; + } + } + + bMaxCharCountIsValid = sal_True; + } + sal_Int16 nRes = eDirection == ConversionDirection_FROM_LEFT ? + nMaxLeftCharCount : nMaxRightCharCount; + DBG_ASSERT( nRes >= 0, "invalid MaxCharCount" ); + return nRes; +} + + +void SAL_CALL ConvDic::setPropertyType( + const OUString& rLeftText, + const OUString& rRightText, + sal_Int16 nPropertyType ) + throw (container::NoSuchElementException, IllegalArgumentException, RuntimeException) +{ + sal_Bool bHasElement = HasEntry( rLeftText, rRightText); + if (!bHasElement) + throw container::NoSuchElementException(); + + // currently we assume that entries with the same left text have the + // same PropertyType even if the right text is different... + if (pConvPropType.get()) + pConvPropType->insert( PropTypeMap::value_type( rLeftText, nPropertyType ) ); + bIsModified = sal_True; +} + + +sal_Int16 SAL_CALL ConvDic::getPropertyType( + const OUString& rLeftText, + const OUString& rRightText ) + throw (container::NoSuchElementException, RuntimeException) +{ + sal_Bool bHasElement = HasEntry( rLeftText, rRightText); + if (!bHasElement) + throw container::NoSuchElementException(); + + sal_Int16 nRes = ConversionPropertyType::NOT_DEFINED; + if (pConvPropType.get()) + { + // still assuming that entries with same left text have same PropertyType + // even if they have different right text... + PropTypeMap::iterator aIt = pConvPropType->find( rLeftText ); + if (aIt != pConvPropType->end()) + nRes = (*aIt).second; + } + return nRes; +} + + +void SAL_CALL ConvDic::flush( ) + throw (RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + + if (!bIsModified) + return; + + Save(); + + // notify listeners + EventObject aEvtObj; + aEvtObj.Source = uno::Reference< XFlushable >( this ); + cppu::OInterfaceIteratorHelper aIt( aFlushListeners ); + while (aIt.hasMoreElements()) + { + uno::Reference< util::XFlushListener > xRef( aIt.next(), UNO_QUERY ); + if (xRef.is()) + xRef->flushed( aEvtObj ); + } +} + + +void SAL_CALL ConvDic::addFlushListener( + const uno::Reference< util::XFlushListener >& rxListener ) + throw (RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + if (rxListener.is()) + aFlushListeners.addInterface( rxListener ); +} + + +void SAL_CALL ConvDic::removeFlushListener( + const uno::Reference< util::XFlushListener >& rxListener ) + throw (RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + if (rxListener.is()) + aFlushListeners.removeInterface( rxListener ); +} + + +OUString SAL_CALL ConvDic::getImplementationName( ) + throw (RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + return getImplementationName_Static(); +} + + +sal_Bool SAL_CALL ConvDic::supportsService( const OUString& rServiceName ) + throw (RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + sal_Bool bRes = sal_False; + if (rServiceName.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM(SN_CONV_DICTIONARY))) + bRes = sal_True; + return bRes; +} + + +uno::Sequence< OUString > SAL_CALL ConvDic::getSupportedServiceNames( ) + throw (RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + return getSupportedServiceNames_Static(); +} + + +uno::Sequence< OUString > ConvDic::getSupportedServiceNames_Static() + throw() +{ + uno::Sequence< OUString > aSNS( 1 ); + aSNS.getArray()[0] = A2OU( SN_CONV_DICTIONARY ); + return aSNS; +} + + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/linguistic/source/convdic.hxx b/linguistic/source/convdic.hxx new file mode 100644 index 000000000000..f33ad751f91b --- /dev/null +++ b/linguistic/source/convdic.hxx @@ -0,0 +1,175 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ +#ifndef _LINGUISTIC_CONVDIC_HXX_ +#define _LINGUISTIC_CONVDIC_HXX_ + +#include <com/sun/star/linguistic2/XConversionDictionary.hpp> +#include <com/sun/star/linguistic2/XConversionPropertyType.hpp> +#include <com/sun/star/util/XFlushable.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <cppuhelper/implbase4.hxx> +#include <cppuhelper/interfacecontainer.h> +#include <tools/string.hxx> + +#include <boost/unordered_map.hpp> +#include <set> +#include <memory> +#include "linguistic/misc.hxx" +#include "defs.hxx" + +// text conversion dictionary extension +#define CONV_DIC_EXT "tcd" +#define CONV_DIC_DOT_EXT ".tcd" + +#define SN_CONV_DICTIONARY "com.sun.star.linguistic2.ConversionDictionary" + + +class SvStream; + +sal_Bool IsConvDic( const String &rFileURL, sal_Int16 &nLang, sal_Int16 &nConvType ); + +struct StrLT +{ + bool operator()( const rtl::OUString &rTxt1, const rtl::OUString &rTxt2 ) const + { + return rTxt1 < rTxt2; + } +}; + +struct StrEQ +{ + bool operator()( const rtl::OUString &rTxt1, const rtl::OUString &rTxt2 ) const + { + return rTxt1 == rTxt2; + } +}; + +typedef boost::unordered_multimap< const rtl::OUString, rtl::OUString, + const rtl::OUStringHash, StrEQ > ConvMap; + +typedef std::set< rtl::OUString, StrLT > ConvMapKeySet; + +typedef boost::unordered_multimap< const rtl::OUString, sal_Int16, + rtl::OUStringHash, StrEQ > PropTypeMap; + + +class ConvDic : + public ::cppu::WeakImplHelper4 + < + ::com::sun::star::linguistic2::XConversionDictionary, + ::com::sun::star::linguistic2::XConversionPropertyType, + ::com::sun::star::util::XFlushable, + ::com::sun::star::lang::XServiceInfo + > +{ + friend class ConvDicXMLExport; + +protected: + + ::cppu::OInterfaceContainerHelper aFlushListeners; + + ConvMap aFromLeft; + std::auto_ptr< ConvMap > pFromRight; // only available for bidirectional conversion dictionaries + + std::auto_ptr< PropTypeMap > pConvPropType; + + String aMainURL; // URL to file + rtl::OUString aName; + sal_Int16 nLanguage; + sal_Int16 nConversionType; + sal_Int16 nMaxLeftCharCount; + sal_Int16 nMaxRightCharCount; + sal_Bool bMaxCharCountIsValid; + sal_Bool bNeedEntries; + sal_Bool bIsModified; + sal_Bool bIsActive; + sal_Bool bIsReadonly; + + // disallow copy-constructor and assignment-operator for now + ConvDic(const ConvDic &); + ConvDic & operator = (const ConvDic &); + + ConvMap::iterator GetEntry( ConvMap &rMap, const rtl::OUString &rFirstText, const rtl::OUString &rSecondText ); + void Load(); + void Save(); + +public: + ConvDic( const String &rName, + sal_Int16 nLanguage, + sal_Int16 nConversionType, + sal_Bool bBiDirectional, + const String &rMainURL); + virtual ~ConvDic(); + + // XConversionDictionary + virtual ::rtl::OUString SAL_CALL getName( ) throw (::com::sun::star::uno::RuntimeException); + virtual ::com::sun::star::lang::Locale SAL_CALL getLocale( ) throw (::com::sun::star::uno::RuntimeException); + virtual sal_Int16 SAL_CALL getConversionType( ) throw (::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL setActive( sal_Bool bActivate ) throw (::com::sun::star::uno::RuntimeException); + virtual sal_Bool SAL_CALL isActive( ) throw (::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL clear( ) throw (::com::sun::star::uno::RuntimeException); + virtual ::com::sun::star::uno::Sequence< ::rtl::OUString > SAL_CALL getConversions( const ::rtl::OUString& aText, sal_Int32 nStartPos, sal_Int32 nLength, ::com::sun::star::linguistic2::ConversionDirection eDirection, sal_Int32 nTextConversionOptions ) throw (::com::sun::star::lang::IllegalArgumentException, ::com::sun::star::uno::RuntimeException); + virtual ::com::sun::star::uno::Sequence< ::rtl::OUString > SAL_CALL getConversionEntries( ::com::sun::star::linguistic2::ConversionDirection eDirection ) throw (::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL addEntry( const ::rtl::OUString& aLeftText, const ::rtl::OUString& aRightText ) throw (::com::sun::star::lang::IllegalArgumentException, ::com::sun::star::container::ElementExistException, ::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL removeEntry( const ::rtl::OUString& aLeftText, const ::rtl::OUString& aRightText ) throw (::com::sun::star::container::NoSuchElementException, ::com::sun::star::uno::RuntimeException); + virtual sal_Int16 SAL_CALL getMaxCharCount( ::com::sun::star::linguistic2::ConversionDirection eDirection ) throw (::com::sun::star::uno::RuntimeException); + + // XConversionPropertyType + virtual void SAL_CALL setPropertyType( const ::rtl::OUString& aLeftText, const ::rtl::OUString& aRightText, ::sal_Int16 nPropertyType ) throw (::com::sun::star::container::NoSuchElementException, ::com::sun::star::lang::IllegalArgumentException, ::com::sun::star::uno::RuntimeException); + virtual ::sal_Int16 SAL_CALL getPropertyType( const ::rtl::OUString& aLeftText, const ::rtl::OUString& aRightText ) throw (::com::sun::star::container::NoSuchElementException, ::com::sun::star::uno::RuntimeException); + + // XFlushable + virtual void SAL_CALL flush( ) throw (::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL addFlushListener( const ::com::sun::star::uno::Reference< ::com::sun::star::util::XFlushListener >& l ) throw (::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL removeFlushListener( const ::com::sun::star::uno::Reference< ::com::sun::star::util::XFlushListener >& l ) throw (::com::sun::star::uno::RuntimeException); + + // XServiceInfo + virtual ::rtl::OUString SAL_CALL getImplementationName( ) throw (::com::sun::star::uno::RuntimeException); + virtual sal_Bool SAL_CALL supportsService( const ::rtl::OUString& ServiceName ) throw (::com::sun::star::uno::RuntimeException); + virtual ::com::sun::star::uno::Sequence< ::rtl::OUString > SAL_CALL getSupportedServiceNames( ) throw (::com::sun::star::uno::RuntimeException); + + + static inline ::rtl::OUString + getImplementationName_Static() throw(); + static com::sun::star::uno::Sequence< ::rtl::OUString > + getSupportedServiceNames_Static() throw(); + + sal_Bool HasEntry( const rtl::OUString &rLeftText, const rtl::OUString &rRightText ); + void AddEntry( const rtl::OUString &rLeftText, const rtl::OUString &rRightText ); + void RemoveEntry( const rtl::OUString &rLeftText, const rtl::OUString &rRightText ); +}; + +inline ::rtl::OUString ConvDic::getImplementationName_Static() throw() +{ + return A2OU( "com.sun.star.lingu2.ConvDic" ); +} + + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/linguistic/source/convdiclist.cxx b/linguistic/source/convdiclist.cxx new file mode 100644 index 000000000000..b240d13e03a6 --- /dev/null +++ b/linguistic/source/convdiclist.cxx @@ -0,0 +1,705 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_linguistic.hxx" + +#include <tools/fsys.hxx> +#include <tools/stream.hxx> +#include <tools/urlobj.hxx> +#include <unotools/pathoptions.hxx> +#include <unotools/useroptions.hxx> +#include <unotools/lingucfg.hxx> +#include <rtl/instance.hxx> +#include <cppuhelper/factory.hxx> // helper for factories +#include <unotools/localfilehelper.hxx> +#include <com/sun/star/linguistic2/XConversionDictionaryList.hpp> +#include <com/sun/star/linguistic2/XConversionDictionary.hpp> +#include <com/sun/star/linguistic2/ConversionDictionaryType.hpp> +#include <com/sun/star/util/XFlushable.hpp> +#include <com/sun/star/lang/Locale.hpp> +#include <com/sun/star/uno/Reference.h> +#include <com/sun/star/registry/XRegistryKey.hpp> +#include <com/sun/star/container/XNameContainer.hpp> + +#include <ucbhelper/content.hxx> + +#include "convdiclist.hxx" +#include "convdic.hxx" +#include "hhconvdic.hxx" +#include "linguistic/misc.hxx" +#include "defs.hxx" + +using namespace osl; +using namespace com::sun::star; +using namespace com::sun::star::lang; +using namespace com::sun::star::uno; +using namespace com::sun::star::container; +using namespace com::sun::star::linguistic2; +using namespace linguistic; + +using ::rtl::OUString; + +#define SN_CONV_DICTIONARY_LIST "com.sun.star.linguistic2.ConversionDictionaryList" + + + +bool operator == ( const Locale &r1, const Locale &r2 ) +{ + return r1.Language == r2.Language && + r1.Country == r2.Country && + r1.Variant == r2.Variant; +} + + +String GetConvDicMainURL( const String &rDicName, const String &rDirectoryURL ) +{ + // build URL to use for new (persistent) dictionaries + + String aFullDicName( rDicName ); + aFullDicName.AppendAscii( CONV_DIC_DOT_EXT ); + + INetURLObject aURLObj; + aURLObj.SetSmartProtocol( INET_PROT_FILE ); + aURLObj.SetSmartURL( rDirectoryURL ); + aURLObj.Append( aFullDicName, INetURLObject::ENCODE_ALL ); + DBG_ASSERT(!aURLObj.HasError(), "invalid URL"); + if (aURLObj.HasError()) + return String(); + else + return aURLObj.GetMainURL( INetURLObject::DECODE_TO_IURI ); +} + + +class ConvDicNameContainer : + public cppu::WeakImplHelper1 + < + ::com::sun::star::container::XNameContainer + > +{ + uno::Sequence< uno::Reference< XConversionDictionary > > aConvDics; + ConvDicList &rConvDicList; + + // disallow copy-constructor and assignment-operator for now + ConvDicNameContainer(const ConvDicNameContainer &); + ConvDicNameContainer & operator = (const ConvDicNameContainer &); + + sal_Int32 GetIndexByName_Impl( const OUString& rName ); + +public: + ConvDicNameContainer( ConvDicList &rMyConvDicList ); + virtual ~ConvDicNameContainer(); + + // XElementAccess + virtual ::com::sun::star::uno::Type SAL_CALL getElementType( ) throw (::com::sun::star::uno::RuntimeException); + virtual sal_Bool SAL_CALL hasElements( ) throw (::com::sun::star::uno::RuntimeException); + + // XNameAccess + virtual ::com::sun::star::uno::Any SAL_CALL getByName( const ::rtl::OUString& aName ) throw (::com::sun::star::container::NoSuchElementException, ::com::sun::star::lang::WrappedTargetException, ::com::sun::star::uno::RuntimeException); + virtual ::com::sun::star::uno::Sequence< ::rtl::OUString > SAL_CALL getElementNames( ) throw (::com::sun::star::uno::RuntimeException); + virtual sal_Bool SAL_CALL hasByName( const ::rtl::OUString& aName ) throw (::com::sun::star::uno::RuntimeException); + + // XNameReplace + virtual void SAL_CALL replaceByName( const ::rtl::OUString& aName, const ::com::sun::star::uno::Any& aElement ) throw (::com::sun::star::lang::IllegalArgumentException, ::com::sun::star::container::NoSuchElementException, ::com::sun::star::lang::WrappedTargetException, ::com::sun::star::uno::RuntimeException); + + // XNameContainer + virtual void SAL_CALL insertByName( const ::rtl::OUString& aName, const ::com::sun::star::uno::Any& aElement ) throw (::com::sun::star::lang::IllegalArgumentException, ::com::sun::star::container::ElementExistException, ::com::sun::star::lang::WrappedTargetException, ::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL removeByName( const ::rtl::OUString& Name ) throw (::com::sun::star::container::NoSuchElementException, ::com::sun::star::lang::WrappedTargetException, ::com::sun::star::uno::RuntimeException); + + + // looks for conversion dictionaries with the specified extension + // in the directory and adds them to the container + void AddConvDics( const String &rSearchDirPathURL, const String &rExtension ); + + // calls Flush for the dictionaries that support XFlushable + void FlushDics() const; + + sal_Int32 GetCount() const { return aConvDics.getLength(); } + uno::Reference< XConversionDictionary > GetByName( const OUString& rName ); + + const uno::Reference< XConversionDictionary > GetByIndex( sal_Int32 nIdx ) + { + return aConvDics.getConstArray()[nIdx]; + } +}; + + +ConvDicNameContainer::ConvDicNameContainer( ConvDicList &rMyConvDicList ) : + rConvDicList( rMyConvDicList ) +{ +} + + +ConvDicNameContainer::~ConvDicNameContainer() +{ +} + + +void ConvDicNameContainer::FlushDics() const +{ + sal_Int32 nLen = aConvDics.getLength(); + const uno::Reference< XConversionDictionary > *pDic = aConvDics.getConstArray(); + for (sal_Int32 i = 0; i < nLen; ++i) + { + uno::Reference< util::XFlushable > xFlush( pDic[i] , UNO_QUERY ); + if (xFlush.is()) + { + try + { + xFlush->flush(); + } + catch(Exception &) + { + OSL_FAIL( "flushing of conversion dictionary failed" ); + } + } + } +} + + +sal_Int32 ConvDicNameContainer::GetIndexByName_Impl( + const OUString& rName ) +{ + sal_Int32 nRes = -1; + sal_Int32 nLen = aConvDics.getLength(); + const uno::Reference< XConversionDictionary > *pDic = aConvDics.getConstArray(); + for (sal_Int32 i = 0; i < nLen && nRes == -1; ++i) + { + if (rName == pDic[i]->getName()) + nRes = i; + } + return nRes; +} + + +uno::Reference< XConversionDictionary > ConvDicNameContainer::GetByName( + const OUString& rName ) +{ + uno::Reference< XConversionDictionary > xRes; + sal_Int32 nIdx = GetIndexByName_Impl( rName ); + if ( nIdx != -1) + xRes = aConvDics.getArray()[nIdx]; + return xRes; +} + + +uno::Type SAL_CALL ConvDicNameContainer::getElementType( ) + throw (RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + return uno::Type( ::getCppuType( (uno::Reference< XConversionDictionary > *) 0) ); +} + + +sal_Bool SAL_CALL ConvDicNameContainer::hasElements( ) + throw (RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + return aConvDics.getLength() > 0; +} + + +uno::Any SAL_CALL ConvDicNameContainer::getByName( const OUString& rName ) + throw (NoSuchElementException, WrappedTargetException, RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + uno::Reference< XConversionDictionary > xRes( GetByName( rName ) ); + if (!xRes.is()) + throw NoSuchElementException(); + return makeAny( xRes ); +} + + +uno::Sequence< OUString > SAL_CALL ConvDicNameContainer::getElementNames( ) + throw (RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + + sal_Int32 nLen = aConvDics.getLength(); + uno::Sequence< OUString > aRes( nLen ); + OUString *pName = aRes.getArray(); + const uno::Reference< XConversionDictionary > *pDic = aConvDics.getConstArray(); + for (sal_Int32 i = 0; i < nLen; ++i) + pName[i] = pDic[i]->getName(); + return aRes; +} + + +sal_Bool SAL_CALL ConvDicNameContainer::hasByName( const OUString& rName ) + throw (RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + return GetByName( rName ).is(); +} + + +void SAL_CALL ConvDicNameContainer::replaceByName( + const OUString& rName, + const uno::Any& rElement ) + throw (IllegalArgumentException, NoSuchElementException, WrappedTargetException, RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + + sal_Int32 nRplcIdx = GetIndexByName_Impl( rName ); + if (nRplcIdx == -1) + throw NoSuchElementException(); + uno::Reference< XConversionDictionary > xNew; + rElement >>= xNew; + if (!xNew.is() || xNew->getName() != rName) + throw IllegalArgumentException(); + aConvDics.getArray()[ nRplcIdx ] = xNew; +} + + +void SAL_CALL ConvDicNameContainer::insertByName( + const OUString& rName, + const Any& rElement ) + throw (IllegalArgumentException, ElementExistException, WrappedTargetException, RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + + if (GetByName( rName ).is()) + throw ElementExistException(); + uno::Reference< XConversionDictionary > xNew; + rElement >>= xNew; + if (!xNew.is() || xNew->getName() != rName) + throw IllegalArgumentException(); + + sal_Int32 nLen = aConvDics.getLength(); + aConvDics.realloc( nLen + 1 ); + aConvDics.getArray()[ nLen ] = xNew; +} + + +void SAL_CALL ConvDicNameContainer::removeByName( const OUString& rName ) + throw (NoSuchElementException, WrappedTargetException, RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + + sal_Int32 nRplcIdx = GetIndexByName_Impl( rName ); + if (nRplcIdx == -1) + throw NoSuchElementException(); + + // physically remove dictionary + uno::Reference< XConversionDictionary > xDel = aConvDics.getArray()[nRplcIdx]; + String aName( xDel->getName() ); + String aDicMainURL( GetConvDicMainURL( aName, GetDictionaryWriteablePath() ) ); + INetURLObject aObj( aDicMainURL ); + DBG_ASSERT( aObj.GetProtocol() == INET_PROT_FILE, "+HangulHanjaOptionsDialog::OkHdl(): non-file URLs cannot be deleted" ); + if( aObj.GetProtocol() == INET_PROT_FILE ) + { + try + { + ::ucbhelper::Content aCnt( aObj.GetMainURL( INetURLObject::NO_DECODE ), + uno::Reference< ::com::sun::star::ucb::XCommandEnvironment > () ); + aCnt.executeCommand( OUString(RTL_CONSTASCII_USTRINGPARAM("delete")), makeAny( sal_Bool( sal_True ) ) ); + } + catch( ::com::sun::star::ucb::CommandAbortedException& ) + { + DBG_ERRORFILE( "HangulHanjaOptionsDialog::OkHdl(): CommandAbortedException" ); + } + catch( ... ) + { + DBG_ERRORFILE( "HangulHanjaOptionsDialog::OkHdl(): Any other exception" ); + } + } + + sal_Int32 nLen = aConvDics.getLength(); + uno::Reference< XConversionDictionary > *pDic = aConvDics.getArray(); + for (sal_Int32 i = nRplcIdx; i < nLen - 1; ++i) + pDic[i] = pDic[i + 1]; + aConvDics.realloc( nLen - 1 ); +} + + +void ConvDicNameContainer::AddConvDics( + const String &rSearchDirPathURL, + const String &rExtension ) +{ + const Sequence< OUString > aDirCnt( + utl::LocalFileHelper::GetFolderContents( rSearchDirPathURL, sal_False ) ); + const OUString *pDirCnt = aDirCnt.getConstArray(); + sal_Int32 nEntries = aDirCnt.getLength(); + + for (sal_Int32 i = 0; i < nEntries; ++i) + { + String aURL( pDirCnt[i] ); + + xub_StrLen nPos = aURL.SearchBackward('.'); + String aExt(aURL.Copy(nPos + 1)); + aExt.ToLowerAscii(); + String aSearchExt( rExtension ); + aSearchExt.ToLowerAscii(); + if(aExt != aSearchExt) + continue; // skip other files + + sal_Int16 nLang; + sal_Int16 nConvType; + if (IsConvDic( aURL, nLang, nConvType )) + { + // get decoded dictionary file name + INetURLObject aURLObj( aURL ); + String aDicName = aURLObj.getBase( INetURLObject::LAST_SEGMENT, + true, INetURLObject::DECODE_WITH_CHARSET, + RTL_TEXTENCODING_UTF8 ); + + uno::Reference < XConversionDictionary > xDic; + if (nLang == LANGUAGE_KOREAN && + nConvType == ConversionDictionaryType::HANGUL_HANJA) + { + xDic = new HHConvDic( aDicName, aURL ); + } + else if ((nLang == LANGUAGE_CHINESE_SIMPLIFIED || nLang == LANGUAGE_CHINESE_TRADITIONAL) && + nConvType == ConversionDictionaryType::SCHINESE_TCHINESE) + { + xDic = new ConvDic( aDicName, nLang, nConvType, sal_False, aURL ); + } + + if (xDic.is()) + { + uno::Any aAny; + aAny <<= xDic; + insertByName( xDic->getName(), aAny ); + } + } + } +} + + +namespace +{ + struct StaticConvDicList : public rtl::StaticWithInit< + uno::Reference<XInterface>, StaticConvDicList> { + uno::Reference<XInterface> operator () () { + return (cppu::OWeakObject *) new ConvDicList; + } + }; +} + + +void ConvDicList::MyAppExitListener::AtExit() +{ + rMyDicList.FlushDics(); + StaticConvDicList::get().clear(); +} + +ConvDicList::ConvDicList() : + aEvtListeners( GetLinguMutex() ) +{ + pNameContainer = 0; + bDisposing = sal_False; + + pExitListener = new MyAppExitListener( *this ); + xExitListener = pExitListener; + pExitListener->Activate(); +} + + +ConvDicList::~ConvDicList() +{ + + if (!bDisposing && pNameContainer) + pNameContainer->FlushDics(); + + pExitListener->Deactivate(); +} + + +void ConvDicList::FlushDics() +{ + // check only pointer to avoid creating the container when + // the dictionaries were not accessed yet + if (pNameContainer) + pNameContainer->FlushDics(); +} + + +ConvDicNameContainer & ConvDicList::GetNameContainer() +{ + if (!pNameContainer) + { + pNameContainer = new ConvDicNameContainer( *this ); + pNameContainer->AddConvDics( GetDictionaryWriteablePath(), + A2OU( CONV_DIC_EXT ) ); + xNameContainer = pNameContainer; + + // access list of text conversion dictionaries to activate + SvtLinguOptions aOpt; + SvtLinguConfig().GetOptions( aOpt ); + sal_Int32 nLen = aOpt.aActiveConvDics.getLength(); + const OUString *pActiveConvDics = aOpt.aActiveConvDics.getConstArray(); + for (sal_Int32 i = 0; i < nLen; ++i) + { + uno::Reference< XConversionDictionary > xDic = + pNameContainer->GetByName( pActiveConvDics[i] ); + if (xDic.is()) + xDic->setActive( sal_True ); + } + + // since there is no UI to active/deactivate the dictionaries + // for chinese text conversion they should be activated by default + uno::Reference< XConversionDictionary > xS2TDic( + pNameContainer->GetByName( A2OU("ChineseS2T") ), UNO_QUERY ); + uno::Reference< XConversionDictionary > xT2SDic( + pNameContainer->GetByName( A2OU("ChineseT2S") ), UNO_QUERY ); + if (xS2TDic.is()) + xS2TDic->setActive( sal_True ); + if (xT2SDic.is()) + xT2SDic->setActive( sal_True ); + + } + return *pNameContainer; +} + + +uno::Reference< container::XNameContainer > SAL_CALL ConvDicList::getDictionaryContainer( ) throw (RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + GetNameContainer(); + DBG_ASSERT( xNameContainer.is(), "missing name container" ); + return xNameContainer; +} + + +uno::Reference< XConversionDictionary > SAL_CALL ConvDicList::addNewDictionary( + const OUString& rName, + const Locale& rLocale, + sal_Int16 nConvDicType ) + throw (NoSupportException, ElementExistException, RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + + sal_Int16 nLang = LocaleToLanguage( rLocale ); + + if (GetNameContainer().hasByName( rName )) + throw ElementExistException(); + + uno::Reference< XConversionDictionary > xRes; + String aDicMainURL( GetConvDicMainURL( rName, GetDictionaryWriteablePath() ) ); + if (nLang == LANGUAGE_KOREAN && + nConvDicType == ConversionDictionaryType::HANGUL_HANJA) + { + xRes = new HHConvDic( rName, aDicMainURL ); + } + else if ((nLang == LANGUAGE_CHINESE_SIMPLIFIED || nLang == LANGUAGE_CHINESE_TRADITIONAL) && + nConvDicType == ConversionDictionaryType::SCHINESE_TCHINESE) + { + xRes = new ConvDic( rName, nLang, nConvDicType, sal_False, aDicMainURL ); + } + + if (!xRes.is()) + throw NoSupportException(); + else + { + xRes->setActive( sal_True ); + uno::Any aAny; + aAny <<= xRes; + GetNameContainer().insertByName( rName, aAny ); + } + return xRes; +} + + +uno::Sequence< OUString > SAL_CALL ConvDicList::queryConversions( + const OUString& rText, + sal_Int32 nStartPos, + sal_Int32 nLength, + const Locale& rLocale, + sal_Int16 nConversionDictionaryType, + ConversionDirection eDirection, + sal_Int32 nTextConversionOptions ) + throw (IllegalArgumentException, NoSupportException, RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + + sal_Int32 nCount = 0; + uno::Sequence< OUString > aRes( 20 ); + OUString *pRes = aRes.getArray(); + + sal_Bool bSupported = sal_False; + sal_Int32 nLen = GetNameContainer().GetCount(); + for (sal_Int32 i = 0; i < nLen; ++i) + { + const uno::Reference< XConversionDictionary > xDic( GetNameContainer().GetByIndex(i) ); + sal_Bool bMatch = xDic.is() && + xDic->getLocale() == rLocale && + xDic->getConversionType() == nConversionDictionaryType; + bSupported |= bMatch; + if (bMatch && xDic->isActive()) + { + Sequence< OUString > aNewConv( xDic->getConversions( + rText, nStartPos, nLength, + eDirection, nTextConversionOptions ) ); + sal_Int32 nNewLen = aNewConv.getLength(); + if (nNewLen > 0) + { + if (nCount + nNewLen > aRes.getLength()) + { + aRes.realloc( nCount + nNewLen + 20 ); + pRes = aRes.getArray(); + } + const OUString *pNewConv = aNewConv.getConstArray(); + for (sal_Int32 k = 0; k < nNewLen; ++k) + pRes[nCount++] = pNewConv[k]; + } + } + } + + if (!bSupported) + throw NoSupportException(); + + aRes.realloc( nCount ); + return aRes; +} + + +sal_Int16 SAL_CALL ConvDicList::queryMaxCharCount( + const Locale& rLocale, + sal_Int16 nConversionDictionaryType, + ConversionDirection eDirection ) + throw (RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + + sal_Int16 nRes = 0; + GetNameContainer(); + sal_Int32 nLen = GetNameContainer().GetCount(); + for (sal_Int32 i = 0; i < nLen; ++i) + { + const uno::Reference< XConversionDictionary > xDic( GetNameContainer().GetByIndex(i) ); + if (xDic.is() && + xDic->getLocale() == rLocale && + xDic->getConversionType() == nConversionDictionaryType) + { + sal_Int16 nC = xDic->getMaxCharCount( eDirection ); + if (nC > nRes) + nRes = nC; + } + } + return nRes; +} + + +void SAL_CALL ConvDicList::dispose( ) + throw (RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + if (!bDisposing) + { + bDisposing = sal_True; + EventObject aEvtObj( (XConversionDictionaryList *) this ); + aEvtListeners.disposeAndClear( aEvtObj ); + + FlushDics(); + } +} + + +void SAL_CALL ConvDicList::addEventListener( + const uno::Reference< XEventListener >& rxListener ) + throw (RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + if (!bDisposing && rxListener.is()) + aEvtListeners.addInterface( rxListener ); +} + + +void SAL_CALL ConvDicList::removeEventListener( + const uno::Reference< XEventListener >& rxListener ) + throw (RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + if (!bDisposing && rxListener.is()) + aEvtListeners.removeInterface( rxListener ); +} + + +OUString SAL_CALL ConvDicList::getImplementationName( ) + throw (RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + return getImplementationName_Static(); +} + + +sal_Bool SAL_CALL ConvDicList::supportsService( const OUString& rServiceName ) + throw (RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + return rServiceName.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM(SN_CONV_DICTIONARY_LIST)); +} + + +uno::Sequence< OUString > SAL_CALL ConvDicList::getSupportedServiceNames( ) + throw (RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + return getSupportedServiceNames_Static(); +} + + +uno::Sequence< OUString > ConvDicList::getSupportedServiceNames_Static() + throw() +{ + uno::Sequence< OUString > aSNS( 1 ); + aSNS.getArray()[0] = A2OU( SN_CONV_DICTIONARY_LIST ); + return aSNS; +} + + + +uno::Reference< uno::XInterface > SAL_CALL ConvDicList_CreateInstance( + const uno::Reference< XMultiServiceFactory > & /*rSMgr*/ ) + throw(Exception) +{ + return StaticConvDicList::get(); +} + +void * SAL_CALL ConvDicList_getFactory( + const sal_Char * pImplName, + XMultiServiceFactory * pServiceManager, void * ) +{ + void * pRet = 0; + if ( !ConvDicList::getImplementationName_Static().compareToAscii( pImplName ) ) + { + uno::Reference< XSingleServiceFactory > xFactory = + cppu::createOneInstanceFactory( + pServiceManager, + ConvDicList::getImplementationName_Static(), + ConvDicList_CreateInstance, + ConvDicList::getSupportedServiceNames_Static()); + // acquire, because we return an interface pointer instead of a reference + xFactory->acquire(); + pRet = xFactory.get(); + } + return pRet; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/linguistic/source/convdiclist.hxx b/linguistic/source/convdiclist.hxx new file mode 100644 index 000000000000..f5f4c74360d8 --- /dev/null +++ b/linguistic/source/convdiclist.hxx @@ -0,0 +1,122 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _LINGUISTIC_CONVDICLIST_HXX_ +#define _LINGUISTIC_CONVDICLIST_HXX_ + +#include <com/sun/star/linguistic2/XConversionDictionaryList.hpp> +#include <com/sun/star/lang/XComponent.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <cppuhelper/implbase3.hxx> +#include <cppuhelper/interfacecontainer.h> +#include <svl/svarray.hxx> +#include <tools/debug.hxx> + +#include "linguistic/misc.hxx" +#include "lngopt.hxx" + + +class ConvDicNameContainer; + + +class ConvDicList : + public cppu::WeakImplHelper3 + < + ::com::sun::star::linguistic2::XConversionDictionaryList, + ::com::sun::star::lang::XComponent, + ::com::sun::star::lang::XServiceInfo + > +{ + + class MyAppExitListener : public linguistic::AppExitListener + { + ConvDicList & rMyDicList; + + public: + MyAppExitListener( ConvDicList &rDicList ) : rMyDicList( rDicList ) {} + virtual void AtExit(); + }; + + + ::cppu::OInterfaceContainerHelper aEvtListeners; + + ConvDicNameContainer *pNameContainer; + ::com::sun::star::uno::Reference< + ::com::sun::star::container::XNameContainer > xNameContainer; + + MyAppExitListener *pExitListener; + ::com::sun::star::uno::Reference< ::com::sun::star::frame:: + XTerminateListener > xExitListener; + + sal_Bool bDisposing; + + // disallow copy-constructor and assignment-operator for now + ConvDicList( const ConvDicList & ); + ConvDicList & operator = (const ConvDicList &); + + ConvDicNameContainer & GetNameContainer(); + +public: + ConvDicList(); + virtual ~ConvDicList(); + + // XConversionDictionaryList + virtual ::com::sun::star::uno::Reference< ::com::sun::star::container::XNameContainer > SAL_CALL getDictionaryContainer( ) throw (::com::sun::star::uno::RuntimeException); + virtual ::com::sun::star::uno::Reference< ::com::sun::star::linguistic2::XConversionDictionary > SAL_CALL addNewDictionary( const ::rtl::OUString& aName, const ::com::sun::star::lang::Locale& aLocale, sal_Int16 nConversionDictionaryType ) throw (::com::sun::star::lang::NoSupportException, ::com::sun::star::container::ElementExistException, ::com::sun::star::uno::RuntimeException); + virtual ::com::sun::star::uno::Sequence< ::rtl::OUString > SAL_CALL queryConversions( const ::rtl::OUString& aText, sal_Int32 nStartPos, sal_Int32 nLength, const ::com::sun::star::lang::Locale& aLocale, sal_Int16 nConversionDictionaryType, ::com::sun::star::linguistic2::ConversionDirection eDirection, sal_Int32 nTextConversionOptions ) throw (::com::sun::star::lang::IllegalArgumentException, ::com::sun::star::lang::NoSupportException, ::com::sun::star::uno::RuntimeException); + virtual sal_Int16 SAL_CALL queryMaxCharCount( const ::com::sun::star::lang::Locale& aLocale, sal_Int16 nConversionDictionaryType, ::com::sun::star::linguistic2::ConversionDirection eDirection ) throw (::com::sun::star::uno::RuntimeException); + + // XComponent + virtual void SAL_CALL dispose( ) throw (::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL addEventListener( const ::com::sun::star::uno::Reference< ::com::sun::star::lang::XEventListener >& xListener ) throw (::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL removeEventListener( const ::com::sun::star::uno::Reference< ::com::sun::star::lang::XEventListener >& aListener ) throw (::com::sun::star::uno::RuntimeException); + + // XServiceInfo + virtual ::rtl::OUString SAL_CALL getImplementationName( ) throw (::com::sun::star::uno::RuntimeException); + virtual sal_Bool SAL_CALL supportsService( const ::rtl::OUString& ServiceName ) throw (::com::sun::star::uno::RuntimeException); + virtual ::com::sun::star::uno::Sequence< ::rtl::OUString > SAL_CALL getSupportedServiceNames( ) throw (::com::sun::star::uno::RuntimeException); + + + static inline ::rtl::OUString + getImplementationName_Static() throw(); + static com::sun::star::uno::Sequence< ::rtl::OUString > + getSupportedServiceNames_Static() throw(); + + // non UNO-specific + void FlushDics(); +}; + +inline ::rtl::OUString ConvDicList::getImplementationName_Static() throw() +{ + return A2OU( "com.sun.star.lingu2.ConvDicList" ); +} + + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/linguistic/source/convdicxml.cxx b/linguistic/source/convdicxml.cxx new file mode 100644 index 000000000000..56ddd94f4487 --- /dev/null +++ b/linguistic/source/convdicxml.cxx @@ -0,0 +1,448 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_linguistic.hxx" +#include <tools/urlobj.hxx> +#include <tools/debug.hxx> +#include <tools/fsys.hxx> +#include <tools/string.hxx> +#include <i18npool/mslangid.hxx> +#include <tools/stream.hxx> +#include <osl/mutex.hxx> +#include <unotools/processfactory.hxx> +#include <ucbhelper/content.hxx> + +#include <cppuhelper/factory.hxx> // helper for factories +#include <com/sun/star/linguistic2/XConversionDictionary.hpp> +#include <com/sun/star/linguistic2/ConversionDictionaryType.hpp> +#include <com/sun/star/linguistic2/ConversionPropertyType.hpp> +#include <com/sun/star/util/XFlushable.hpp> +#include <com/sun/star/lang/Locale.hpp> +#include <com/sun/star/lang/EventObject.hpp> +#include <com/sun/star/uno/Reference.h> +#include <com/sun/star/registry/XRegistryKey.hpp> +#include <com/sun/star/util/XFlushListener.hpp> +#include <com/sun/star/io/XActiveDataSource.hpp> +#include <com/sun/star/document/XFilter.hpp> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <xmloff/nmspmap.hxx> +#include <xmloff/xmlnmspe.hxx> +#include <unotools/streamwrap.hxx> + +#include "convdic.hxx" +#include "convdicxml.hxx" +#include "linguistic/misc.hxx" +#include "defs.hxx" + +using namespace std; +using namespace utl; +using namespace osl; +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; + +using ::rtl::OUString; + +#define XML_NAMESPACE_TCD_STRING "http://openoffice.org/2003/text-conversion-dictionary" +#define CONV_TYPE_HANGUL_HANJA "Hangul / Hanja" +#define CONV_TYPE_SCHINESE_TCHINESE "Chinese simplified / Chinese traditional" + + +static const OUString ConversionTypeToText( sal_Int16 nConversionType ) +{ + OUString aRes; + if (nConversionType == ConversionDictionaryType::HANGUL_HANJA) + aRes = A2OU( CONV_TYPE_HANGUL_HANJA ); + else if (nConversionType == ConversionDictionaryType::SCHINESE_TCHINESE) + aRes = A2OU( CONV_TYPE_SCHINESE_TCHINESE ); + return aRes; +} + +static sal_Int16 GetConversionTypeFromText( const String &rText ) +{ + sal_Int16 nRes = -1; + if (rText.EqualsAscii( CONV_TYPE_HANGUL_HANJA )) + nRes = ConversionDictionaryType::HANGUL_HANJA; + else if (rText.EqualsAscii( CONV_TYPE_SCHINESE_TCHINESE )) + nRes = ConversionDictionaryType::SCHINESE_TCHINESE; + return nRes; +} + + +class ConvDicXMLImportContext : + public SvXMLImportContext +{ +public: + ConvDicXMLImportContext( + ConvDicXMLImport &rImport, + sal_uInt16 nPrfx, const OUString& rLName ) : + SvXMLImportContext( rImport, nPrfx, rLName ) + { + } + + const ConvDicXMLImport & GetConvDicImport() const + { + return (const ConvDicXMLImport &) GetImport(); + } + + ConvDicXMLImport & GetConvDicImport() + { + return (ConvDicXMLImport &) GetImport(); + } + + // SvXMLImportContext + virtual void Characters( const OUString &rChars ); + virtual SvXMLImportContext * CreateChildContext( sal_uInt16 nPrefix, const OUString& rLocalName, const uno::Reference< xml::sax::XAttributeList > &rxAttrList); +}; + + +class ConvDicXMLDictionaryContext_Impl : + public ConvDicXMLImportContext +{ + sal_Int16 nLanguage; + sal_Int16 nConversionType; + +public: + ConvDicXMLDictionaryContext_Impl( ConvDicXMLImport &rImport, + sal_uInt16 nPrefix, const OUString& rLName) : + ConvDicXMLImportContext( rImport, nPrefix, rLName ) + { + nLanguage = LANGUAGE_NONE; + nConversionType = -1; + } + + // SvXMLImportContext + virtual void StartElement( const ::com::sun::star::uno::Reference< ::com::sun::star::xml::sax::XAttributeList >& xAttrList ); + virtual SvXMLImportContext * CreateChildContext( sal_uInt16 nPrefix, const OUString& rLocalName, const uno::Reference< xml::sax::XAttributeList > &rxAttrList ); + + sal_Int16 GetLanguage() const { return nLanguage; } + sal_Int16 GetConversionType() const { return nConversionType; } +}; + + +class ConvDicXMLEntryTextContext_Impl : + public ConvDicXMLImportContext +{ + OUString aLeftText; + sal_Int16 nPropertyType; // used for Chinese simplified/traditional conversion + ConvDicXMLDictionaryContext_Impl &rDicContext; + +public: + ConvDicXMLEntryTextContext_Impl( + ConvDicXMLImport &rImport, + sal_uInt16 nPrefix, const OUString& rLName, + ConvDicXMLDictionaryContext_Impl &rParentContext ) : + ConvDicXMLImportContext( rImport, nPrefix, rLName ), + nPropertyType( ConversionPropertyType::NOT_DEFINED ), + rDicContext( rParentContext ) + { + } + + // SvXMLImportContext + virtual void StartElement( const ::com::sun::star::uno::Reference< ::com::sun::star::xml::sax::XAttributeList >& xAttrList ); + virtual SvXMLImportContext * CreateChildContext( sal_uInt16 nPrefix, const OUString& rLocalName, const uno::Reference< xml::sax::XAttributeList > &rxAttrList ); + + const OUString & GetLeftText() const { return aLeftText; } + sal_Int16 GetPropertyType() const { return nPropertyType; } + void SetPropertyType( sal_Int16 nVal ) { nPropertyType = nVal; } +}; + + +class ConvDicXMLRightTextContext_Impl : + public ConvDicXMLImportContext +{ + OUString aRightText; + ConvDicXMLEntryTextContext_Impl &rEntryContext; + +public: + ConvDicXMLRightTextContext_Impl( + ConvDicXMLImport &rImport, + sal_uInt16 nPrefix, const OUString& rLName, + ConvDicXMLEntryTextContext_Impl &rParentContext ) : + ConvDicXMLImportContext( rImport, nPrefix, rLName ), + rEntryContext( rParentContext ) + { + } + + // SvXMLImportContext + virtual void EndElement(); + virtual SvXMLImportContext * CreateChildContext( sal_uInt16 nPrefix, const OUString& rLocalName, const uno::Reference< xml::sax::XAttributeList > &rxAttrList ); + virtual void Characters( const OUString &rChars ); + + const OUString & GetRightText() const { return aRightText; } + const OUString & GetLeftText() const { return rEntryContext.GetLeftText(); } + ConvDic * GetDic() { return GetConvDicImport().GetDic(); } +}; + + +void ConvDicXMLImportContext::Characters(const OUString & /*rChars*/) +{ + /* + Whitespace occurring within the content of token elements is "trimmed" + from the ends (i.e. all whitespace at the beginning and end of the + content is removed), and "collapsed" internally (i.e. each sequence of + 1 or more whitespace characters is replaced with one blank character). + */ + //collapsing not done yet! + +} + +SvXMLImportContext * ConvDicXMLImportContext::CreateChildContext( + sal_uInt16 nPrefix, const OUString& rLocalName, + const uno::Reference< xml::sax::XAttributeList > & /*rxAttrList*/ ) +{ + SvXMLImportContext *pContext = 0; + if (nPrefix == XML_NAMESPACE_TCD && rLocalName.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("text-conversion-dictionary"))) + pContext = new ConvDicXMLDictionaryContext_Impl( GetConvDicImport(), nPrefix, rLocalName ); + else + pContext = new SvXMLImportContext( GetImport(), nPrefix, rLocalName ); + return pContext; +} + + +void ConvDicXMLDictionaryContext_Impl::StartElement( + const uno::Reference< xml::sax::XAttributeList > &rxAttrList ) +{ + sal_Int16 nAttrCount = rxAttrList.is() ? rxAttrList->getLength() : 0; + for (sal_Int16 i = 0; i < nAttrCount; ++i) + { + OUString aAttrName = rxAttrList->getNameByIndex(i); + OUString aLocalName; + sal_uInt16 nPrefix = GetImport().GetNamespaceMap(). + GetKeyByAttrName( aAttrName, &aLocalName ); + OUString aValue = rxAttrList->getValueByIndex(i); + + if (nPrefix == XML_NAMESPACE_TCD && aLocalName.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("lang"))) + nLanguage = MsLangId::convertIsoStringToLanguage( aValue ); + else if (nPrefix == XML_NAMESPACE_TCD && aLocalName.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("conversion-type"))) + nConversionType = GetConversionTypeFromText( aValue ); + } + GetConvDicImport().SetLanguage( nLanguage ); + GetConvDicImport().SetConversionType( nConversionType ); + +} + +SvXMLImportContext * ConvDicXMLDictionaryContext_Impl::CreateChildContext( + sal_uInt16 nPrefix, const OUString& rLocalName, + const uno::Reference< xml::sax::XAttributeList > & /*rxAttrList*/ ) +{ + SvXMLImportContext *pContext = 0; + if (nPrefix == XML_NAMESPACE_TCD && rLocalName.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("entry"))) + pContext = new ConvDicXMLEntryTextContext_Impl( GetConvDicImport(), nPrefix, rLocalName, *this ); + else + pContext = new SvXMLImportContext( GetImport(), nPrefix, rLocalName ); + return pContext; +} + + +SvXMLImportContext * ConvDicXMLEntryTextContext_Impl::CreateChildContext( + sal_uInt16 nPrefix, const OUString& rLocalName, + const uno::Reference< xml::sax::XAttributeList > & /*rxAttrList*/ ) +{ + SvXMLImportContext *pContext = 0; + if (nPrefix == XML_NAMESPACE_TCD && rLocalName.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("right-text"))) + pContext = new ConvDicXMLRightTextContext_Impl( GetConvDicImport(), nPrefix, rLocalName, *this ); + else + pContext = new SvXMLImportContext( GetImport(), nPrefix, rLocalName ); + return pContext; +} + +void ConvDicXMLEntryTextContext_Impl::StartElement( + const uno::Reference< xml::sax::XAttributeList >& rxAttrList ) +{ + sal_Int16 nAttrCount = rxAttrList.is() ? rxAttrList->getLength() : 0; + for (sal_Int16 i = 0; i < nAttrCount; ++i) + { + OUString aAttrName = rxAttrList->getNameByIndex(i); + OUString aLocalName; + sal_uInt16 nPrefix = GetImport().GetNamespaceMap(). + GetKeyByAttrName( aAttrName, &aLocalName ); + OUString aValue = rxAttrList->getValueByIndex(i); + + if (nPrefix == XML_NAMESPACE_TCD && aLocalName.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("left-text"))) + aLeftText = aValue; + if (nPrefix == XML_NAMESPACE_TCD && aLocalName.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("property-type"))) + nPropertyType = (sal_Int16) aValue.toInt32(); + } +} + + +SvXMLImportContext * ConvDicXMLRightTextContext_Impl::CreateChildContext( + sal_uInt16 nPrefix, const OUString& rLocalName, + const uno::Reference< xml::sax::XAttributeList > & /*rxAttrList*/ ) +{ + // leaf: return default (empty) context + SvXMLImportContext *pContext = new SvXMLImportContext( GetImport(), nPrefix, rLocalName ); + return pContext; +} + +void ConvDicXMLRightTextContext_Impl::Characters( const OUString &rChars ) +{ + aRightText += rChars; +} + +void ConvDicXMLRightTextContext_Impl::EndElement() +{ + ConvDic *pDic = GetDic(); + if (pDic) + pDic->AddEntry( GetLeftText(), GetRightText() ); +} + + + +sal_Bool ConvDicXMLExport::Export() +{ + sal_Bool bRet = sal_False; + + uno::Reference< document::XExporter > xExporter( this ); + uno::Reference< document::XFilter > xFilter( xExporter, UNO_QUERY ); + uno::Sequence< beans::PropertyValue > aProps(0); + xFilter->filter( aProps ); // calls exportDoc implicitly + + return bRet = bSuccess; +} + + +sal_uInt32 ConvDicXMLExport::exportDoc( enum ::xmloff::token::XMLTokenEnum /*eClass*/ ) +{ + _GetNamespaceMap().Add( A2OU( "tcd" ), + A2OU( XML_NAMESPACE_TCD_STRING ), XML_NAMESPACE_TCD ); + + GetDocHandler()->startDocument(); + + // Add xmlns line and some other arguments + AddAttribute( _GetNamespaceMap().GetAttrNameByKey( XML_NAMESPACE_TCD ), + _GetNamespaceMap().GetNameByKey( XML_NAMESPACE_TCD ) ); + AddAttributeASCII( XML_NAMESPACE_TCD, "package", "org.openoffice.Office" ); + + OUString aIsoLang( MsLangId::convertLanguageToIsoString( rDic.nLanguage ) ); + AddAttribute( XML_NAMESPACE_TCD, "lang", aIsoLang ); + OUString aConvType( ConversionTypeToText( rDic.nConversionType ) ); + AddAttribute( XML_NAMESPACE_TCD, "conversion-type", aConvType ); + + //!! block necessary in order to have SvXMLElementExport d-tor called + //!! before the call to endDocument + { + SvXMLElementExport aRoot( *this, XML_NAMESPACE_TCD, "text-conversion-dictionary", sal_True, sal_True ); + _ExportContent(); + } + + GetDocHandler()->endDocument(); + + bSuccess = sal_True; + return 0; +} + + +void ConvDicXMLExport::_ExportContent() +{ + // aquire sorted list of all keys + ConvMapKeySet aKeySet; + ConvMap::iterator aIt; + for (aIt = rDic.aFromLeft.begin(); aIt != rDic.aFromLeft.end(); ++aIt) + aKeySet.insert( (*aIt).first ); + + ConvMapKeySet::iterator aKeyIt; + for (aKeyIt = aKeySet.begin(); aKeyIt != aKeySet.end(); ++aKeyIt) + { + OUString aLeftText( *aKeyIt ); + AddAttribute( XML_NAMESPACE_TCD, "left-text", aLeftText ); + if (rDic.pConvPropType.get()) // property-type list available? + { + sal_Int16 nPropertyType = -1; + PropTypeMap::iterator aIt2 = rDic.pConvPropType->find( aLeftText ); + if (aIt2 != rDic.pConvPropType->end()) + nPropertyType = (*aIt2).second; + DBG_ASSERT( nPropertyType, "property-type not found" ); + if (nPropertyType == -1) + nPropertyType = ConversionPropertyType::NOT_DEFINED; + AddAttribute( XML_NAMESPACE_TCD, "property-type", OUString::valueOf( (sal_Int32) nPropertyType ) ); + } + SvXMLElementExport aEntryMain( *this, XML_NAMESPACE_TCD, + "entry" , sal_True, sal_True ); + + pair< ConvMap::iterator, ConvMap::iterator > aRange = + rDic.aFromLeft.equal_range( *aKeyIt ); + for (aIt = aRange.first; aIt != aRange.second; ++aIt) + { + DBG_ASSERT( *aKeyIt == (*aIt).first, "key <-> entry mismatch" ); + OUString aRightText( (*aIt).second ); + SvXMLElementExport aEntryRightText( *this, XML_NAMESPACE_TCD, + "right-text" , sal_True, sal_False ); + Characters( aRightText ); + } + } +} + +::rtl::OUString SAL_CALL ConvDicXMLExport::getImplementationName() + throw( uno::RuntimeException ) +{ + return A2OU( "com.sun.star.lingu2.ConvDicXMLExport" ); +} + + +void SAL_CALL ConvDicXMLImport::startDocument(void) + throw( xml::sax::SAXException, uno::RuntimeException ) +{ + // register namespace at first possible opportunity + GetNamespaceMap().Add( A2OU( "tcd" ), + A2OU( XML_NAMESPACE_TCD_STRING ), XML_NAMESPACE_TCD ); + SvXMLImport::startDocument(); +} + +void SAL_CALL ConvDicXMLImport::endDocument(void) + throw( xml::sax::SAXException, uno::RuntimeException ) +{ + SvXMLImport::endDocument(); +} + +SvXMLImportContext * ConvDicXMLImport::CreateContext( + sal_uInt16 nPrefix, + const rtl::OUString &rLocalName, + const uno::Reference < xml::sax::XAttributeList > & /*rxAttrList*/ ) +{ + SvXMLImportContext *pContext = 0; + if (nPrefix == XML_NAMESPACE_TCD && rLocalName.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("text-conversion-dictionary"))) + pContext = new ConvDicXMLDictionaryContext_Impl( *this, nPrefix, rLocalName ); + else + pContext = new SvXMLImportContext( *this, nPrefix, rLocalName ); + return pContext; +} + + +OUString SAL_CALL ConvDicXMLImport::getImplementationName() + throw( uno::RuntimeException ) +{ + return A2OU( "com.sun.star.lingu2.ConvDicXMLImport" ); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/linguistic/source/convdicxml.hxx b/linguistic/source/convdicxml.hxx new file mode 100644 index 000000000000..c93965d65dcf --- /dev/null +++ b/linguistic/source/convdicxml.hxx @@ -0,0 +1,131 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _LINGUISTIC_CONVDICXML_HXX_ +#define _LINGUISTIC_CONVDICXML_HXX_ + +#include <com/sun/star/linguistic2/XConversionDictionary.hpp> +#include <com/sun/star/util/XFlushable.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/xml/sax/XDocumentHandler.hpp> +#include <xmloff/xmlexp.hxx> +#include <xmloff/xmlimp.hxx> +#include <cppuhelper/implbase3.hxx> +#include <cppuhelper/interfacecontainer.h> +#include <tools/string.hxx> +#include <rtl/ustring.hxx> +#include "linguistic/misc.hxx" +#include "defs.hxx" + + +class ConvDic; + + +class ConvDicXMLExport : public SvXMLExport +{ + ConvDic &rDic; + sal_Bool bSuccess; + +public: + ConvDicXMLExport( ConvDic &rConvDic, + const rtl::OUString &rFileName, + com::sun::star::uno::Reference< com::sun::star::xml::sax::XDocumentHandler > &rHandler) : + SvXMLExport ( utl::getProcessServiceFactory(), rFileName, rHandler ), + rDic ( rConvDic ), + bSuccess ( sal_False ) + { + } + virtual ~ConvDicXMLExport() + { + } + + // XServiceInfo (override parent method) + ::rtl::OUString SAL_CALL getImplementationName() throw( ::com::sun::star::uno::RuntimeException ); + + // SvXMLExport + void _ExportAutoStyles() {} + void _ExportMasterStyles() {} + void _ExportContent(); + sal_uInt32 exportDoc( enum ::xmloff::token::XMLTokenEnum eClass ); + + sal_Bool Export(); +}; + + +class ConvDicXMLImport : public SvXMLImport +{ + ConvDic *pDic; // conversion dictionary to be used + // if != NULL: whole file will be read and + // all entries will be added to the dictionary + // if == NULL: no entries will be added + // but the language and conversion type will + // still be determined! + + sal_Int16 nLanguage; // language of the dictionary + sal_Int16 nConversionType; // conversion type the dictionary is used for + sal_Bool bSuccess; + +public: + + //!! see comment for pDic member + ConvDicXMLImport( ConvDic *pConvDic, const rtl::OUString /*&rFileName*/ ) : + SvXMLImport ( utl::getProcessServiceFactory(), IMPORT_ALL ), + pDic ( pConvDic ) + { + nLanguage = LANGUAGE_NONE; + nConversionType = -1; + bSuccess = sal_False; + } + + virtual ~ConvDicXMLImport() throw () + { + } + + // XServiceInfo (override parent method) + ::rtl::OUString SAL_CALL getImplementationName() throw( ::com::sun::star::uno::RuntimeException ); + + virtual void SAL_CALL startDocument(void) throw( ::com::sun::star::xml::sax::SAXException, ::com::sun::star::uno::RuntimeException ); + virtual void SAL_CALL endDocument(void) throw( ::com::sun::star::xml::sax::SAXException, ::com::sun::star::uno::RuntimeException ); + + virtual SvXMLImportContext * CreateContext( + sal_uInt16 nPrefix, const rtl::OUString &rLocalName, + const com::sun::star::uno::Reference < com::sun::star::xml::sax::XAttributeList > &rxAttrList ); + + ConvDic * GetDic() { return pDic; } + sal_Int16 GetLanguage() const { return nLanguage; } + sal_Int16 GetConversionType() const { return nConversionType; } + sal_Bool GetSuccess() const { return bSuccess; } + + void SetLanguage( sal_Int16 nLang ) { nLanguage = nLang; } + void SetConversionType( sal_Int16 nType ) { nConversionType = nType; } +}; + + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/linguistic/source/defs.hxx b/linguistic/source/defs.hxx new file mode 100644 index 000000000000..14c0ce2f2853 --- /dev/null +++ b/linguistic/source/defs.hxx @@ -0,0 +1,132 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _LINGUISTIC_DEFS_HXX_ +#define _LINGUISTIC_DEFS_HXX_ + +#include <com/sun/star/linguistic2/XSpellChecker.hpp> +#include <com/sun/star/linguistic2/XProofreader.hpp> +#include <com/sun/star/linguistic2/XHyphenator.hpp> +#include <com/sun/star/linguistic2/XThesaurus.hpp> + +#include <boost/shared_ptr.hpp> + +class SvStream; + + + +typedef boost::shared_ptr< SvStream > SvStreamPtr; + +namespace css = ::com::sun::star; + + +struct LangSvcEntries +{ + css::uno::Sequence< ::rtl::OUString > aSvcImplNames; + + sal_Int16 nLastTriedSvcIndex; + bool bAlreadyWarned; + bool bDoWarnAgain; + + LangSvcEntries() : nLastTriedSvcIndex(-1), bAlreadyWarned(false), bDoWarnAgain(false) {} + + inline LangSvcEntries( const css::uno::Sequence< ::rtl::OUString > &rSvcImplNames ) : + aSvcImplNames(rSvcImplNames), + nLastTriedSvcIndex(-1), bAlreadyWarned(false), bDoWarnAgain(false) + { + } + + inline LangSvcEntries( const ::rtl::OUString &rSvcImplName ) : + nLastTriedSvcIndex(-1), bAlreadyWarned(false), bDoWarnAgain(false) + { + aSvcImplNames.realloc(1); + aSvcImplNames[0] = rSvcImplName; + } + + bool IsAlreadyWarned() const { return bAlreadyWarned != 0; } + void SetAlreadyWarned( bool bVal ) { bAlreadyWarned = 0 != bVal; } + bool IsDoWarnAgain() const { return bDoWarnAgain != 0; } + void SetDoWarnAgain( bool bVal ) { bDoWarnAgain = 0 != bVal; } + + inline void Clear() + { + aSvcImplNames.realloc(0); + nLastTriedSvcIndex = -1; + bAlreadyWarned = false; + bDoWarnAgain = false; + } +}; + +struct LangSvcEntries_Spell : public LangSvcEntries +{ + css::uno::Sequence< css::uno::Reference< css::linguistic2::XSpellChecker > > aSvcRefs; + + LangSvcEntries_Spell() : LangSvcEntries() {} + LangSvcEntries_Spell( const css::uno::Sequence< ::rtl::OUString > &rSvcImplNames ) : LangSvcEntries( rSvcImplNames ) {} +}; + +struct LangSvcEntries_Grammar : public LangSvcEntries +{ + css::uno::Sequence< css::uno::Reference< css::linguistic2::XProofreader > > aSvcRefs; + + LangSvcEntries_Grammar() : LangSvcEntries() {} + LangSvcEntries_Grammar( const ::rtl::OUString &rSvcImplName ) : LangSvcEntries( rSvcImplName ) {} +}; + +struct LangSvcEntries_Hyph : public LangSvcEntries +{ + css::uno::Sequence< css::uno::Reference< css::linguistic2::XHyphenator > > aSvcRefs; + + LangSvcEntries_Hyph() : LangSvcEntries() {} + LangSvcEntries_Hyph( const ::rtl::OUString &rSvcImplName ) : LangSvcEntries( rSvcImplName ) {} +}; + +struct LangSvcEntries_Thes : public LangSvcEntries +{ + css::uno::Sequence< css::uno::Reference< css::linguistic2::XThesaurus > > aSvcRefs; + + LangSvcEntries_Thes() : LangSvcEntries() {} + LangSvcEntries_Thes( const css::uno::Sequence< ::rtl::OUString > &rSvcImplNames ) : LangSvcEntries( rSvcImplNames ) {} +}; + + +// virtual base class for the different dispatchers +class LinguDispatcher +{ +public: + enum DspType { DSP_SPELL, DSP_HYPH, DSP_THES, DSP_GRAMMAR }; + + virtual void SetServiceList( const css::lang::Locale &rLocale, const css::uno::Sequence< rtl::OUString > &rSvcImplNames ) = 0; + virtual css::uno::Sequence< rtl::OUString > GetServiceList( const css::lang::Locale &rLocale ) const = 0; + virtual DspType GetDspType() const = 0; +}; + + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/linguistic/source/dicimp.cxx b/linguistic/source/dicimp.cxx new file mode 100644 index 000000000000..462e2da3c782 --- /dev/null +++ b/linguistic/source/dicimp.cxx @@ -0,0 +1,1148 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 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 com::sun::star; +using namespace com::sun::star::lang; +using namespace com::sun::star::uno; +using namespace com::sun::star::linguistic2; +using namespace linguistic; + +using ::rtl::OUString; + + +#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); + + // read header + 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 ]; + + // Read the first word + 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()) + { + // Read from file + // Paste in dictionary without converting + 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()) + 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(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; +} + + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/linguistic/source/dicimp.hxx b/linguistic/source/dicimp.hxx new file mode 100644 index 000000000000..e40a0840002b --- /dev/null +++ b/linguistic/source/dicimp.hxx @@ -0,0 +1,232 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _LINGUISTIC_DICIMP_HXX_ +#define _LINGUISTIC_DICIMP_HXX_ + +#include <com/sun/star/linguistic2/XDictionary.hpp> +#include <com/sun/star/frame/XStorable.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> + +#include <uno/lbnames.h> // CPPU_CURRENT_LANGUAGE_BINDING_NAME macro, which specify the environment type +#include <cppuhelper/implbase2.hxx> // helper for implementations +#include <cppuhelper/implbase1.hxx> // helper for implementations +#include <cppuhelper/interfacecontainer.h> +#include <tools/string.hxx> +#include <tools/stream.hxx> + +#include "defs.hxx" +#include "linguistic/misc.hxx" + +#define DIC_MAX_ENTRIES 30000 + +sal_Int16 ReadDicVersion( SvStreamPtr &rpStream, sal_uInt16 &nLng, sal_Bool &bNeg ); +const String GetDicExtension(); + +class DictionaryNeo : + public ::cppu::WeakImplHelper2 + < + ::com::sun::star::linguistic2::XDictionary, + ::com::sun::star::frame::XStorable + > +{ + + ::cppu::OInterfaceContainerHelper aDicEvtListeners; + ::com::sun::star::uno::Sequence< + ::com::sun::star::uno::Reference< + ::com::sun::star::linguistic2::XDictionaryEntry > > aEntries; + ::rtl::OUString aDicName; + ::rtl::OUString aMainURL; + ::com::sun::star::linguistic2::DictionaryType eDicType; + sal_Int16 nCount; + sal_Int16 nLanguage; + sal_Int16 nDicVersion; + sal_Bool bNeedEntries; + sal_Bool bIsModified; + sal_Bool bIsActive; + sal_Bool bIsReadonly; + + // disallow copy-constructor and assignment-operator for now + DictionaryNeo(const DictionaryNeo &); + DictionaryNeo & operator = (const DictionaryNeo &); + + void launchEvent(sal_Int16 nEvent, + ::com::sun::star::uno::Reference< + ::com::sun::star::linguistic2::XDictionaryEntry > xEntry); + + sal_uLong loadEntries(const ::rtl::OUString &rMainURL); + sal_uLong saveEntries(const ::rtl::OUString &rMainURL); + int cmpDicEntry(const ::rtl::OUString &rWord1, + const ::rtl::OUString &rWord2, + sal_Bool bSimilarOnly = sal_False); + sal_Bool seekEntry(const ::rtl::OUString &rWord, sal_Int32 *pPos, + sal_Bool bSimilarOnly = sal_False); + sal_Bool isSorted(); + + sal_Bool addEntry_Impl(const ::com::sun::star::uno::Reference< + ::com::sun::star::linguistic2::XDictionaryEntry > xDicEntry, + sal_Bool bIsLoadEntries = sal_False); + +public: + DictionaryNeo(); + DictionaryNeo(const ::rtl::OUString &rName, sal_Int16 nLang, + ::com::sun::star::linguistic2::DictionaryType eType, + const ::rtl::OUString &rMainURL, + sal_Bool bWriteable ); + virtual ~DictionaryNeo(); + + // XNamed + virtual ::rtl::OUString SAL_CALL + getName() + throw(::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL + setName( const ::rtl::OUString& aName ) + throw(::com::sun::star::uno::RuntimeException); + + // XDictionary + virtual ::com::sun::star::linguistic2::DictionaryType SAL_CALL + getDictionaryType() + throw(::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL + setActive( sal_Bool bActivate ) + throw(::com::sun::star::uno::RuntimeException); + virtual sal_Bool SAL_CALL + isActive() + throw(::com::sun::star::uno::RuntimeException); + virtual sal_Int32 SAL_CALL + getCount() + throw(::com::sun::star::uno::RuntimeException); + virtual ::com::sun::star::lang::Locale SAL_CALL + getLocale() + throw(::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL + setLocale( const ::com::sun::star::lang::Locale& aLocale ) + throw(::com::sun::star::uno::RuntimeException); + virtual ::com::sun::star::uno::Reference< + ::com::sun::star::linguistic2::XDictionaryEntry > SAL_CALL + getEntry( const ::rtl::OUString& aWord ) + throw(::com::sun::star::uno::RuntimeException); + virtual sal_Bool SAL_CALL + addEntry( const ::com::sun::star::uno::Reference< + ::com::sun::star::linguistic2::XDictionaryEntry >& xDicEntry ) + throw(::com::sun::star::uno::RuntimeException); + virtual sal_Bool SAL_CALL + add( const ::rtl::OUString& aWord, sal_Bool bIsNegative, + const ::rtl::OUString& aRplcText ) + throw(::com::sun::star::uno::RuntimeException); + virtual sal_Bool SAL_CALL + remove( const ::rtl::OUString& aWord ) + throw(::com::sun::star::uno::RuntimeException); + virtual sal_Bool SAL_CALL + isFull() + throw(::com::sun::star::uno::RuntimeException); + virtual ::com::sun::star::uno::Sequence< + ::com::sun::star::uno::Reference< + ::com::sun::star::linguistic2::XDictionaryEntry > > SAL_CALL + getEntries() + throw(::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL + clear() + throw(::com::sun::star::uno::RuntimeException); + virtual sal_Bool SAL_CALL + addDictionaryEventListener( const ::com::sun::star::uno::Reference< + ::com::sun::star::linguistic2::XDictionaryEventListener >& xListener ) + throw(::com::sun::star::uno::RuntimeException); + virtual sal_Bool SAL_CALL + removeDictionaryEventListener( const ::com::sun::star::uno::Reference< + ::com::sun::star::linguistic2::XDictionaryEventListener >& xListener ) + throw(::com::sun::star::uno::RuntimeException); + + // XStorable + virtual sal_Bool SAL_CALL + hasLocation() + throw(::com::sun::star::uno::RuntimeException); + virtual ::rtl::OUString SAL_CALL + getLocation() + throw(::com::sun::star::uno::RuntimeException); + virtual sal_Bool SAL_CALL + isReadonly() + throw(::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL + store() + throw(::com::sun::star::io::IOException, + ::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL + storeAsURL( const ::rtl::OUString& aURL, + const ::com::sun::star::uno::Sequence< + ::com::sun::star::beans::PropertyValue >& aArgs ) + throw(::com::sun::star::io::IOException, + ::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL + storeToURL( const ::rtl::OUString& aURL, + const ::com::sun::star::uno::Sequence< + ::com::sun::star::beans::PropertyValue >& aArgs ) + throw(::com::sun::star::io::IOException, + ::com::sun::star::uno::RuntimeException); +}; + + + +class DicEntry : + public cppu::WeakImplHelper1 + < + ::com::sun::star::linguistic2::XDictionaryEntry + > +{ + ::rtl::OUString aDicWord, // including hyphen positions represented by "=" + aReplacement; // including hyphen positions represented by "=" + sal_Bool bIsNegativ; + + // disallow copy-constructor and assignment-operator for now + DicEntry(const DicEntry &); + DicEntry & operator = (const DicEntry &); + + void splitDicFileWord(const ::rtl::OUString &rDicFileWord, + ::rtl::OUString &rDicWord, + ::rtl::OUString &rReplacement); + +public: + DicEntry(const ::rtl::OUString &rDicFileWord, sal_Bool bIsNegativ); + DicEntry(const ::rtl::OUString &rDicWord, sal_Bool bIsNegativ, + const ::rtl::OUString &rRplcText); + virtual ~DicEntry(); + + // XDictionaryEntry + virtual ::rtl::OUString SAL_CALL + getDictionaryWord() throw(::com::sun::star::uno::RuntimeException); + virtual sal_Bool SAL_CALL + isNegative() throw(::com::sun::star::uno::RuntimeException); + virtual ::rtl::OUString SAL_CALL + getReplacementText() throw(::com::sun::star::uno::RuntimeException); +}; + + + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/linguistic/source/dlistimp.cxx b/linguistic/source/dlistimp.cxx new file mode 100644 index 000000000000..319cd9d56be8 --- /dev/null +++ b/linguistic/source/dlistimp.cxx @@ -0,0 +1,933 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_linguistic.hxx" + +#include <cppuhelper/factory.hxx> +#include <i18npool/mslangid.hxx> +#include <osl/file.hxx> +#include <tools/fsys.hxx> +#include <tools/stream.hxx> +#include <tools/urlobj.hxx> +#include <i18npool/mslangid.hxx> +#include <unotools/pathoptions.hxx> +#include <unotools/useroptions.hxx> +#include <cppuhelper/factory.hxx> // helper for factories +#include <unotools/localfilehelper.hxx> +#include <comphelper/processfactory.hxx> +#include <unotools/ucbstreamhelper.hxx> +#include <com/sun/star/frame/XStorable.hpp> +#include <com/sun/star/lang/Locale.hpp> +#include <com/sun/star/uno/Reference.h> +#include <com/sun/star/linguistic2/DictionaryEventFlags.hpp> +#include <com/sun/star/linguistic2/DictionaryListEventFlags.hpp> +#include <com/sun/star/registry/XRegistryKey.hpp> +#include <com/sun/star/ucb/XSimpleFileAccess.hpp> + +#include "defs.hxx" +#include "dlistimp.hxx" +#include "dicimp.hxx" +#include "lngopt.hxx" + +#include "defs.hxx" +#include "dlistimp.hxx" +#include "dicimp.hxx" +#include "lngopt.hxx" + +using namespace osl; +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; + +using ::rtl::OUString; + + +static sal_Bool IsVers2OrNewer( const String& rFileURL, sal_uInt16& nLng, sal_Bool& bNeg ); + +static void AddInternal( const uno::Reference< XDictionary > &rDic, + const rtl::OUString& rNew ); +static void AddUserData( const uno::Reference< XDictionary > &rDic ); + + +class DicEvtListenerHelper : + public cppu::WeakImplHelper1 + < + XDictionaryEventListener + > +{ + cppu::OInterfaceContainerHelper aDicListEvtListeners; + uno::Sequence< DictionaryEvent > aCollectDicEvt; + uno::Reference< XDictionaryList > xMyDicList; + + sal_Int16 nCondensedEvt; + sal_Int16 nNumCollectEvtListeners, + nNumVerboseListeners; + +public: + DicEvtListenerHelper( const uno::Reference< XDictionaryList > &rxDicList ); + virtual ~DicEvtListenerHelper(); + + // XEventListener + virtual void SAL_CALL + disposing( const EventObject& rSource ) + throw(RuntimeException); + + // XDictionaryEventListener + virtual void SAL_CALL + processDictionaryEvent( const DictionaryEvent& rDicEvent ) + throw(RuntimeException); + + // non-UNO functions + void DisposeAndClear( const EventObject &rEvtObj ); + + sal_Bool AddDicListEvtListener( + const uno::Reference< XDictionaryListEventListener >& rxListener, + sal_Bool bReceiveVerbose ); + sal_Bool RemoveDicListEvtListener( + const uno::Reference< XDictionaryListEventListener >& rxListener ); + sal_Int16 BeginCollectEvents(); + sal_Int16 EndCollectEvents(); + sal_Int16 FlushEvents(); + void ClearEvents() { nCondensedEvt = 0; } +}; + + +DicEvtListenerHelper::DicEvtListenerHelper( + const uno::Reference< XDictionaryList > &rxDicList ) : + aDicListEvtListeners ( GetLinguMutex() ), + xMyDicList ( rxDicList ) +{ + nCondensedEvt = 0; + nNumCollectEvtListeners = nNumVerboseListeners = 0; +} + + +DicEvtListenerHelper::~DicEvtListenerHelper() +{ + DBG_ASSERT(aDicListEvtListeners.getLength() == 0, + "lng : event listeners are still existing"); +} + + +void DicEvtListenerHelper::DisposeAndClear( const EventObject &rEvtObj ) +{ + aDicListEvtListeners.disposeAndClear( rEvtObj ); +} + + +void SAL_CALL DicEvtListenerHelper::disposing( const EventObject& rSource ) + throw(RuntimeException) +{ + osl::MutexGuard aGuard( GetLinguMutex() ); + + uno::Reference< XInterface > xSrc( rSource.Source ); + + // remove event object from EventListener list + if (xSrc.is()) + aDicListEvtListeners.removeInterface( xSrc ); + + // if object is a dictionary then remove it from the dictionary list + // Note: this will probably happen only if someone makes a XDictionary + // implementation of his own that is also a XComponent. + uno::Reference< XDictionary > xDic( xSrc, UNO_QUERY ); + if (xDic.is()) + { + xMyDicList->removeDictionary( xDic ); + } +} + + +void SAL_CALL DicEvtListenerHelper::processDictionaryEvent( + const DictionaryEvent& rDicEvent ) + throw(RuntimeException) +{ + osl::MutexGuard aGuard( GetLinguMutex() ); + + uno::Reference< XDictionary > xDic( rDicEvent.Source, UNO_QUERY ); + DBG_ASSERT(xDic.is(), "lng : missing event source"); + + // assert that there is a corresponding dictionary entry if one was + // added or deleted + uno::Reference< XDictionaryEntry > xDicEntry( rDicEvent.xDictionaryEntry, UNO_QUERY ); + DBG_ASSERT( !(rDicEvent.nEvent & + (DictionaryEventFlags::ADD_ENTRY | DictionaryEventFlags::DEL_ENTRY)) + || xDicEntry.is(), + "lng : missing dictionary entry" ); + + // evaluate DictionaryEvents and update data for next DictionaryListEvent + DictionaryType eDicType = xDic->getDictionaryType(); + DBG_ASSERT(eDicType != DictionaryType_MIXED, + "lng : unexpected dictionary type"); + if ((rDicEvent.nEvent & DictionaryEventFlags::ADD_ENTRY) && xDic->isActive()) + nCondensedEvt |= xDicEntry->isNegative() ? + DictionaryListEventFlags::ADD_NEG_ENTRY : + DictionaryListEventFlags::ADD_POS_ENTRY; + if ((rDicEvent.nEvent & DictionaryEventFlags::DEL_ENTRY) && xDic->isActive()) + nCondensedEvt |= xDicEntry->isNegative() ? + DictionaryListEventFlags::DEL_NEG_ENTRY : + DictionaryListEventFlags::DEL_POS_ENTRY; + if ((rDicEvent.nEvent & DictionaryEventFlags::ENTRIES_CLEARED) && xDic->isActive()) + nCondensedEvt |= eDicType == DictionaryType_NEGATIVE ? + DictionaryListEventFlags::DEL_NEG_ENTRY : + DictionaryListEventFlags::DEL_POS_ENTRY; + if ((rDicEvent.nEvent & DictionaryEventFlags::CHG_LANGUAGE) && xDic->isActive()) + nCondensedEvt |= eDicType == DictionaryType_NEGATIVE ? + DictionaryListEventFlags::DEACTIVATE_NEG_DIC + | DictionaryListEventFlags::ACTIVATE_NEG_DIC : + DictionaryListEventFlags::DEACTIVATE_POS_DIC + | DictionaryListEventFlags::ACTIVATE_POS_DIC; + if ((rDicEvent.nEvent & DictionaryEventFlags::ACTIVATE_DIC)) + nCondensedEvt |= eDicType == DictionaryType_NEGATIVE ? + DictionaryListEventFlags::ACTIVATE_NEG_DIC : + DictionaryListEventFlags::ACTIVATE_POS_DIC; + if ((rDicEvent.nEvent & DictionaryEventFlags::DEACTIVATE_DIC)) + nCondensedEvt |= eDicType == DictionaryType_NEGATIVE ? + DictionaryListEventFlags::DEACTIVATE_NEG_DIC : + DictionaryListEventFlags::DEACTIVATE_POS_DIC; + + // update list of collected events if needs to be + if (nNumVerboseListeners > 0) + { + sal_Int32 nColEvts = aCollectDicEvt.getLength(); + aCollectDicEvt.realloc( nColEvts + 1 ); + aCollectDicEvt.getArray()[ nColEvts ] = rDicEvent; + } + + if (nNumCollectEvtListeners == 0 && nCondensedEvt != 0) + FlushEvents(); +} + + +sal_Bool DicEvtListenerHelper::AddDicListEvtListener( + const uno::Reference< XDictionaryListEventListener >& xListener, + sal_Bool /*bReceiveVerbose*/ ) +{ + DBG_ASSERT( xListener.is(), "empty reference" ); + sal_Int32 nCount = aDicListEvtListeners.getLength(); + return aDicListEvtListeners.addInterface( xListener ) != nCount; +} + + +sal_Bool DicEvtListenerHelper::RemoveDicListEvtListener( + const uno::Reference< XDictionaryListEventListener >& xListener ) +{ + DBG_ASSERT( xListener.is(), "empty reference" ); + sal_Int32 nCount = aDicListEvtListeners.getLength(); + return aDicListEvtListeners.removeInterface( xListener ) != nCount; +} + + +sal_Int16 DicEvtListenerHelper::BeginCollectEvents() +{ + return ++nNumCollectEvtListeners; +} + + +sal_Int16 DicEvtListenerHelper::EndCollectEvents() +{ + DBG_ASSERT(nNumCollectEvtListeners > 0, "lng: mismatched function call"); + if (nNumCollectEvtListeners > 0) + { + FlushEvents(); + nNumCollectEvtListeners--; + } + + return nNumCollectEvtListeners; +} + + +sal_Int16 DicEvtListenerHelper::FlushEvents() +{ + if (0 != nCondensedEvt) + { + // build DictionaryListEvent to pass on to listeners + uno::Sequence< DictionaryEvent > aDicEvents; + if (nNumVerboseListeners > 0) + aDicEvents = aCollectDicEvt; + DictionaryListEvent aEvent( xMyDicList, nCondensedEvt, aDicEvents ); + + // pass on event + cppu::OInterfaceIteratorHelper aIt( aDicListEvtListeners ); + while (aIt.hasMoreElements()) + { + uno::Reference< XDictionaryListEventListener > xRef( aIt.next(), UNO_QUERY ); + if (xRef.is()) + xRef->processDictionaryListEvent( aEvent ); + } + + // clear "list" of events + nCondensedEvt = 0; + aCollectDicEvt.realloc( 0 ); + } + + return nNumCollectEvtListeners; +} + + + + +void DicList::MyAppExitListener::AtExit() +{ + rMyDicList.SaveDics(); +} + + +DicList::DicList() : + aEvtListeners ( GetLinguMutex() ) +{ + pDicEvtLstnrHelper = new DicEvtListenerHelper( this ); + xDicEvtLstnrHelper = pDicEvtLstnrHelper; + bDisposing = sal_False; + bInCreation = sal_False; + + pExitListener = new MyAppExitListener( *this ); + xExitListener = pExitListener; + pExitListener->Activate(); +} + +DicList::~DicList() +{ + pExitListener->Deactivate(); +} + + +void DicList::SearchForDictionaries( + DictionaryVec_t&rDicList, + const String &rDicDirURL, + sal_Bool bIsWriteablePath ) +{ + osl::MutexGuard aGuard( GetLinguMutex() ); + + const uno::Sequence< rtl::OUString > aDirCnt( utl::LocalFileHelper:: + GetFolderContents( rDicDirURL, sal_False ) ); + const rtl::OUString *pDirCnt = aDirCnt.getConstArray(); + sal_Int32 nEntries = aDirCnt.getLength(); + + String aDCN( String::CreateFromAscii( "dcn" ) ); + String aDCP( String::CreateFromAscii( "dcp" ) ); + for (sal_Int32 i = 0; i < nEntries; ++i) + { + String aURL( pDirCnt[i] ); + sal_uInt16 nLang = LANGUAGE_NONE; + sal_Bool bNeg = sal_False; + + if(!::IsVers2OrNewer( aURL, nLang, bNeg )) + { + // When not + xub_StrLen nPos = aURL.Search('.'); + String aExt(aURL.Copy(nPos + 1)); + aExt.ToLowerAscii(); + + if(aExt == aDCN) // negativ + bNeg = sal_True; + else if(aExt == aDCP) // positiv + bNeg = sal_False; + else + continue; // andere Files + } + + // Record in the list of Dictoinaries + // When it already exists don't record + sal_Int16 nSystemLanguage = MsLangId::getSystemLanguage(); + String aTmp1 = ToLower( aURL, nSystemLanguage ); + xub_StrLen nPos = aTmp1.SearchBackward( '/' ); + if (STRING_NOTFOUND != nPos) + aTmp1 = aTmp1.Copy( nPos + 1 ); + String aTmp2; + size_t j; + size_t nCount = rDicList.size(); + for(j = 0; j < nCount; j++) + { + aTmp2 = rDicList[j]->getName().getStr(); + aTmp2 = ToLower( aTmp2, nSystemLanguage ); + if(aTmp1 == aTmp2) + break; + } + if(j >= nCount) // dictionary not yet in DicList + { + // get decoded dictionary file name + INetURLObject aURLObj( aURL ); + String aDicName = aURLObj.getName( INetURLObject::LAST_SEGMENT, + true, INetURLObject::DECODE_WITH_CHARSET, + RTL_TEXTENCODING_UTF8 ); + + DictionaryType eType = bNeg ? DictionaryType_NEGATIVE : DictionaryType_POSITIVE; + uno::Reference< XDictionary > xDic = + new DictionaryNeo( aDicName, nLang, eType, aURL, bIsWriteablePath ); + + addDictionary( xDic ); + nCount++; + } + } +} + + +sal_Int32 DicList::GetDicPos(const uno::Reference< XDictionary > &xDic) +{ + osl::MutexGuard aGuard( GetLinguMutex() ); + + sal_Int32 nPos = -1; + DictionaryVec_t& rDicList = GetOrCreateDicList(); + size_t n = rDicList.size(); + for (size_t i = 0; i < n; i++) + { + if ( rDicList[i] == xDic ) + return i; + } + return nPos; +} + + +uno::Reference< XInterface > SAL_CALL + DicList_CreateInstance( const uno::Reference< XMultiServiceFactory > & /*rSMgr*/ ) + throw(Exception) +{ + uno::Reference< XInterface > xService = (cppu::OWeakObject *) new DicList; + return xService; +} + +sal_Int16 SAL_CALL DicList::getCount() throw(RuntimeException) +{ + osl::MutexGuard aGuard( GetLinguMutex() ); + return static_cast< sal_Int16 >(GetOrCreateDicList().size()); +} + +uno::Sequence< uno::Reference< XDictionary > > SAL_CALL + DicList::getDictionaries() + throw(RuntimeException) +{ + osl::MutexGuard aGuard( GetLinguMutex() ); + + DictionaryVec_t& rDicList = GetOrCreateDicList(); + + uno::Sequence< uno::Reference< XDictionary > > aDics( rDicList.size() ); + uno::Reference< XDictionary > *pDic = aDics.getArray(); + + sal_Int32 n = (sal_uInt16) aDics.getLength(); + for (sal_Int32 i = 0; i < n; i++) + pDic[i] = rDicList[i]; + + return aDics; +} + +uno::Reference< XDictionary > SAL_CALL + DicList::getDictionaryByName( const rtl::OUString& aDictionaryName ) + throw(RuntimeException) +{ + osl::MutexGuard aGuard( GetLinguMutex() ); + + uno::Reference< XDictionary > xDic; + DictionaryVec_t& rDicList = GetOrCreateDicList(); + size_t nCount = rDicList.size(); + for (size_t i = 0; i < nCount; i++) + { + const uno::Reference< XDictionary > &rDic = rDicList[i]; + if (rDic.is() && rDic->getName() == aDictionaryName) + { + xDic = rDic; + break; + } + } + + return xDic; +} + +sal_Bool SAL_CALL DicList::addDictionary( + const uno::Reference< XDictionary >& xDictionary ) + throw(RuntimeException) +{ + osl::MutexGuard aGuard( GetLinguMutex() ); + + if (bDisposing) + return sal_False; + + sal_Bool bRes = sal_False; + if (xDictionary.is()) + { + DictionaryVec_t& rDicList = GetOrCreateDicList(); + rDicList.push_back( xDictionary ); + bRes = sal_True; + + // add listener helper to the dictionaries listener lists + xDictionary->addDictionaryEventListener( xDicEvtLstnrHelper ); + } + return bRes; +} + +sal_Bool SAL_CALL + DicList::removeDictionary( const uno::Reference< XDictionary >& xDictionary ) + throw(RuntimeException) +{ + osl::MutexGuard aGuard( GetLinguMutex() ); + + if (bDisposing) + return sal_False; + + sal_Bool bRes = sal_False; + sal_Int32 nPos = GetDicPos( xDictionary ); + if (nPos >= 0) + { + // remove dictionary list from the dictionaries listener lists + DictionaryVec_t& rDicList = GetOrCreateDicList(); + uno::Reference< XDictionary > xDic( rDicList[ nPos ] ); + DBG_ASSERT(xDic.is(), "lng : empty reference"); + if (xDic.is()) + { + // deactivate dictionary if not already done + xDic->setActive( sal_False ); + + xDic->removeDictionaryEventListener( xDicEvtLstnrHelper ); + } + + // remove element at nPos + rDicList.erase( rDicList.begin() + nPos ); + bRes = sal_True; + } + return bRes; +} + +sal_Bool SAL_CALL DicList::addDictionaryListEventListener( + const uno::Reference< XDictionaryListEventListener >& xListener, + sal_Bool bReceiveVerbose ) + throw(RuntimeException) +{ + osl::MutexGuard aGuard( GetLinguMutex() ); + + if (bDisposing) + return sal_False; + + DBG_ASSERT(!bReceiveVerbose, "lng : not yet supported"); + + sal_Bool bRes = sal_False; + if (xListener.is()) //! don't add empty references + { + bRes = pDicEvtLstnrHelper-> + AddDicListEvtListener( xListener, bReceiveVerbose ); + } + return bRes; +} + +sal_Bool SAL_CALL DicList::removeDictionaryListEventListener( + const uno::Reference< XDictionaryListEventListener >& xListener ) + throw(RuntimeException) +{ + osl::MutexGuard aGuard( GetLinguMutex() ); + + if (bDisposing) + return sal_False; + + sal_Bool bRes = sal_False; + if(xListener.is()) + { + bRes = pDicEvtLstnrHelper->RemoveDicListEvtListener( xListener ); + } + return bRes; +} + +sal_Int16 SAL_CALL DicList::beginCollectEvents() throw(RuntimeException) +{ + osl::MutexGuard aGuard( GetLinguMutex() ); + return pDicEvtLstnrHelper->BeginCollectEvents(); +} + +sal_Int16 SAL_CALL DicList::endCollectEvents() throw(RuntimeException) +{ + osl::MutexGuard aGuard( GetLinguMutex() ); + return pDicEvtLstnrHelper->EndCollectEvents(); +} + +sal_Int16 SAL_CALL DicList::flushEvents() throw(RuntimeException) +{ + osl::MutexGuard aGuard( GetLinguMutex() ); + return pDicEvtLstnrHelper->FlushEvents(); +} + +uno::Reference< XDictionary > SAL_CALL + DicList::createDictionary( const rtl::OUString& rName, const Locale& rLocale, + DictionaryType eDicType, const rtl::OUString& rURL ) + throw(RuntimeException) +{ + osl::MutexGuard aGuard( GetLinguMutex() ); + + sal_Int16 nLanguage = LocaleToLanguage( rLocale ); + bool bIsWriteablePath = rURL.match( GetDictionaryWriteablePath(), 0 ); + return new DictionaryNeo( rName, nLanguage, eDicType, rURL, bIsWriteablePath ); +} + + +uno::Reference< XDictionaryEntry > SAL_CALL + DicList::queryDictionaryEntry( const rtl::OUString& rWord, const Locale& rLocale, + sal_Bool bSearchPosDics, sal_Bool bSearchSpellEntry ) + throw(RuntimeException) +{ + osl::MutexGuard aGuard( GetLinguMutex() ); + return SearchDicList( this, rWord, LocaleToLanguage( rLocale ), + bSearchPosDics, bSearchSpellEntry ); +} + + +void SAL_CALL + DicList::dispose() + throw(RuntimeException) +{ + osl::MutexGuard aGuard( GetLinguMutex() ); + + if (!bDisposing) + { + bDisposing = sal_True; + EventObject aEvtObj( (XDictionaryList *) this ); + + aEvtListeners.disposeAndClear( aEvtObj ); + if (pDicEvtLstnrHelper) + pDicEvtLstnrHelper->DisposeAndClear( aEvtObj ); + + //! avoid creation of dictionaries if not already done + if (aDicList.size() > 0) + { + DictionaryVec_t& rDicList = GetOrCreateDicList(); + size_t nCount = rDicList.size(); + for (size_t i = 0; i < nCount; i++) + { + uno::Reference< XDictionary > xDic( rDicList[i], UNO_QUERY ); + + // save (modified) dictionaries + uno::Reference< frame::XStorable > xStor( xDic , UNO_QUERY ); + if (xStor.is()) + { + try + { + if (!xStor->isReadonly() && xStor->hasLocation()) + xStor->store(); + } + catch(Exception &) + { + } + } + + // release references to (members of) this object hold by + // dictionaries + if (xDic.is()) + xDic->removeDictionaryEventListener( xDicEvtLstnrHelper ); + } + } + xDicEvtLstnrHelper.clear(); + } +} + +void SAL_CALL + DicList::addEventListener( const uno::Reference< XEventListener >& rxListener ) + throw(RuntimeException) +{ + osl::MutexGuard aGuard( GetLinguMutex() ); + + if (!bDisposing && rxListener.is()) + aEvtListeners.addInterface( rxListener ); +} + +void SAL_CALL + DicList::removeEventListener( const uno::Reference< XEventListener >& rxListener ) + throw(RuntimeException) +{ + osl::MutexGuard aGuard( GetLinguMutex() ); + + if (!bDisposing && rxListener.is()) + aEvtListeners.removeInterface( rxListener ); +} + +void DicList::_CreateDicList() +{ + bInCreation = sal_True; + + // look for dictionaries + const rtl::OUString aWriteablePath( GetDictionaryWriteablePath() ); + uno::Sequence< rtl::OUString > aPaths( GetDictionaryPaths() ); + const rtl::OUString *pPaths = aPaths.getConstArray(); + for (sal_Int32 i = 0; i < aPaths.getLength(); ++i) + { + const sal_Bool bIsWriteablePath = (pPaths[i] == aWriteablePath); + SearchForDictionaries( aDicList, pPaths[i], bIsWriteablePath ); + } + + // create IgnoreAllList dictionary with empty URL (non persistent) + // and add it to list + rtl::OUString aDicName( A2OU( "IgnoreAllList" ) ); + uno::Reference< XDictionary > xIgnAll( + createDictionary( aDicName, CreateLocale( LANGUAGE_NONE ), + DictionaryType_POSITIVE, rtl::OUString() ) ); + if (xIgnAll.is()) + { + AddUserData( xIgnAll ); + xIgnAll->setActive( sal_True ); + addDictionary( xIgnAll ); + } + + + // evaluate list of dictionaries to be activated from configuration + //! to suppress overwriting the list of active dictionaries in the + //! configuration with incorrect arguments during the following + //! activation of the dictionaries + pDicEvtLstnrHelper->BeginCollectEvents(); + const uno::Sequence< rtl::OUString > aActiveDics( aOpt.GetActiveDics() ); + const rtl::OUString *pActiveDic = aActiveDics.getConstArray(); + sal_Int32 nLen = aActiveDics.getLength(); + for (sal_Int32 i = 0; i < nLen; ++i) + { + if (pActiveDic[i].getLength()) + { + uno::Reference< XDictionary > xDic( getDictionaryByName( pActiveDic[i] ) ); + if (xDic.is()) + xDic->setActive( sal_True ); + } + } + + // suppress collected events during creation of the dictionary list. + // there should be no events during creation. + pDicEvtLstnrHelper->ClearEvents(); + + pDicEvtLstnrHelper->EndCollectEvents(); + + bInCreation = sal_False; +} + + +void DicList::SaveDics() +{ + // save dics only if they have already been used/created. + //! don't create them just for the purpose of saving them ! + if (aDicList.size() > 0) + { + // save (modified) dictionaries + DictionaryVec_t& rDicList = GetOrCreateDicList(); + size_t nCount = rDicList.size();; + for (size_t i = 0; i < nCount; i++) + { + // save (modified) dictionaries + uno::Reference< frame::XStorable > xStor( rDicList[i], UNO_QUERY ); + if (xStor.is()) + { + try + { + if (!xStor->isReadonly() && xStor->hasLocation()) + xStor->store(); + } + catch(Exception &) + { + } + } + } + } +} + + +// Service specific part + +rtl::OUString SAL_CALL DicList::getImplementationName( ) throw(RuntimeException) +{ + osl::MutexGuard aGuard( GetLinguMutex() ); + return getImplementationName_Static(); +} + + +sal_Bool SAL_CALL DicList::supportsService( const rtl::OUString& ServiceName ) + throw(RuntimeException) +{ + osl::MutexGuard aGuard( GetLinguMutex() ); + + uno::Sequence< rtl::OUString > aSNL = getSupportedServiceNames(); + const rtl::OUString * pArray = aSNL.getConstArray(); + for( sal_Int32 i = 0; i < aSNL.getLength(); i++ ) + if( pArray[i] == ServiceName ) + return sal_True; + return sal_False; +} + + +uno::Sequence< rtl::OUString > SAL_CALL DicList::getSupportedServiceNames( ) + throw(RuntimeException) +{ + osl::MutexGuard aGuard( GetLinguMutex() ); + return getSupportedServiceNames_Static(); +} + + +uno::Sequence< rtl::OUString > DicList::getSupportedServiceNames_Static() throw() +{ + osl::MutexGuard aGuard( GetLinguMutex() ); + + uno::Sequence< rtl::OUString > aSNS( 1 ); // more than 1 service possible + aSNS.getArray()[0] = A2OU( SN_DICTIONARY_LIST ); + return aSNS; +} + +void * SAL_CALL DicList_getFactory( const sal_Char * pImplName, + XMultiServiceFactory * pServiceManager, void * ) +{ + void * pRet = 0; + if ( !DicList::getImplementationName_Static().compareToAscii( pImplName ) ) + { + uno::Reference< XSingleServiceFactory > xFactory = + cppu::createOneInstanceFactory( + pServiceManager, + DicList::getImplementationName_Static(), + DicList_CreateInstance, + DicList::getSupportedServiceNames_Static()); + // acquire, because we return an interface pointer instead of a reference + xFactory->acquire(); + pRet = xFactory.get(); + } + return pRet; +} + + +xub_StrLen lcl_GetToken( String &rToken, + const String &rText, xub_StrLen nPos, const String &rDelim ) +{ + xub_StrLen nRes = STRING_LEN; + + if (rText.Len() == 0 || nPos >= rText.Len()) + rToken = String(); + else if (rDelim.Len() == 0) + { + rToken = rText; + if (rToken.Len()) + nRes = rText.Len(); + } + else + { + xub_StrLen i; + for (i = nPos; i < rText.Len(); ++i) + { + if (STRING_NOTFOUND != rDelim.Search( rText.GetChar(i) )) + break; + } + + if (i >= rText.Len()) // delimeter not found + rToken = rText.Copy( nPos ); + else + rToken = rText.Copy( nPos, sal::static_int_cast< xub_StrLen >((sal_Int32) i - nPos) ); + nRes = i + 1; // continue after found delimeter + } + + return nRes; +} + + +static void AddInternal( + const uno::Reference<XDictionary> &rDic, + const rtl::OUString& rNew ) +{ + if (rDic.is()) + { + //! TL TODO: word iterator should be used to break up the text + static const char *pDefWordDelim = + "!\"#$%&'()*+,-./:;<=>?[]\\_^`{|}~\t \n"; + ByteString aDummy( pDefWordDelim ); + String aDelim( aDummy, osl_getThreadTextEncoding() ); + aDelim.EraseAllChars( '.' ); + + String aToken; + xub_StrLen nPos = 0; + while (STRING_LEN != + (nPos = lcl_GetToken( aToken, rNew, nPos, aDelim ))) + { + if( aToken.Len() && !IsNumeric( aToken ) ) + { + rDic->add( aToken, sal_False, rtl::OUString() ); + } + } + } +} + +static void AddUserData( const uno::Reference< XDictionary > &rDic ) +{ + if (rDic.is()) + { + SvtUserOptions aUserOpt; + AddInternal( rDic, aUserOpt.GetFullName() ); + AddInternal( rDic, aUserOpt.GetCompany() ); + AddInternal( rDic, aUserOpt.GetStreet() ); + AddInternal( rDic, aUserOpt.GetCity() ); + AddInternal( rDic, aUserOpt.GetTitle() ); + AddInternal( rDic, aUserOpt.GetPosition() ); + AddInternal( rDic, aUserOpt.GetEmail() ); + } +} + + +#if defined _MSC_VER +#pragma optimize("g",off) +#endif + +static sal_Bool IsVers2OrNewer( const String& rFileURL, sal_uInt16& nLng, sal_Bool& bNeg ) +{ + if (rFileURL.Len() == 0) + return sal_False; + String aDIC( GetDicExtension() ); + String aExt; + xub_StrLen nPos = rFileURL.SearchBackward( '.' ); + if (STRING_NOTFOUND != nPos) + aExt = rFileURL.Copy( nPos + 1 ); + aExt.ToLowerAscii(); + + if(aExt != aDIC) + return sal_False; + + // get stream to be used + uno::Reference< lang::XMultiServiceFactory > xServiceFactory( comphelper::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( rFileURL ); + } + catch (uno::Exception & e) + { + DBG_ASSERT( 0, "failed to get input stream" ); + (void) e; + } + DBG_ASSERT( xStream.is(), "failed to get stream for read" ); + if (!xStream.is()) + return sal_False; + + SvStreamPtr pStream = SvStreamPtr( utl::UcbStreamHelper::CreateStream( xStream ) ); + + int nDicVersion = ReadDicVersion(pStream, nLng, bNeg); + if (2 == nDicVersion || nDicVersion >= 5) + return sal_True; + + return sal_False; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/linguistic/source/dlistimp.hxx b/linguistic/source/dlistimp.hxx new file mode 100644 index 000000000000..ca8df5a3b2c4 --- /dev/null +++ b/linguistic/source/dlistimp.hxx @@ -0,0 +1,151 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _LINGUISTIC_DLISTIMP_HXX_ +#define _LINGUISTIC_DLISTIMP_HXX_ + +#include <com/sun/star/linguistic2/XSearchableDictionaryList.hpp> +#include <com/sun/star/lang/XComponent.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> + +#include <uno/lbnames.h> // CPPU_CURRENT_LANGUAGE_BINDING_NAME macro, which specify the environment type +#include <cppuhelper/implbase1.hxx> // helper for implementations +#include <cppuhelper/implbase3.hxx> // helper for implementations +#include <cppuhelper/interfacecontainer.h> +#include <tools/debug.hxx> + +#include <vector> +#include <memory> + +#include "linguistic/misc.hxx" +#include "lngopt.hxx" + +class DicEvtListenerHelper; + + +class DicList : + public cppu::WeakImplHelper3 + < + ::com::sun::star::linguistic2::XSearchableDictionaryList, + ::com::sun::star::lang::XComponent, + ::com::sun::star::lang::XServiceInfo + > +{ + class MyAppExitListener : public linguistic::AppExitListener + { + DicList & rMyDicList; + + public: + MyAppExitListener( DicList &rDicList ) : rMyDicList( rDicList ) {} + virtual void AtExit(); + }; + + LinguOptions aOpt; + + ::cppu::OInterfaceContainerHelper aEvtListeners; + + typedef std::vector< ::com::sun::star::uno::Reference< ::com::sun::star::linguistic2::XDictionary > > DictionaryVec_t; + DictionaryVec_t aDicList; + + ::com::sun::star::uno::Reference< ::com::sun::star::linguistic2:: + XDictionaryEventListener > xDicEvtLstnrHelper; + DicEvtListenerHelper *pDicEvtLstnrHelper; + + ::com::sun::star::uno::Reference< ::com::sun::star::frame:: + XTerminateListener > xExitListener; + MyAppExitListener *pExitListener; + + sal_Bool bDisposing; + sal_Bool bInCreation; + + // disallow copy-constructor and assignment-operator for now + DicList( const DicList & ); + DicList & operator = (const DicList &); + + void _CreateDicList(); + DictionaryVec_t & GetOrCreateDicList() + { + if (!bInCreation && aDicList.size() == 0) + _CreateDicList(); + return aDicList; + } + + void LaunchEvent(sal_Int16 nEvent, com::sun::star::uno::Sequence< + ::com::sun::star::linguistic2::XDictionary > xDic); + void SearchForDictionaries( DictionaryVec_t &rDicList, + const String &rDicDir, sal_Bool bIsWritePath ); + sal_Int32 GetDicPos(const com::sun::star::uno::Reference< + ::com::sun::star::linguistic2::XDictionary > &xDic); + +public: + DicList(); + virtual ~DicList(); + + // XDictionaryList + virtual ::sal_Int16 SAL_CALL getCount( ) throw (::com::sun::star::uno::RuntimeException); + virtual ::com::sun::star::uno::Sequence< ::com::sun::star::uno::Reference< ::com::sun::star::linguistic2::XDictionary > > SAL_CALL getDictionaries( ) throw (::com::sun::star::uno::RuntimeException); + virtual ::com::sun::star::uno::Reference< ::com::sun::star::linguistic2::XDictionary > SAL_CALL getDictionaryByName( const ::rtl::OUString& aDictionaryName ) throw (::com::sun::star::uno::RuntimeException); + virtual ::sal_Bool SAL_CALL addDictionary( const ::com::sun::star::uno::Reference< ::com::sun::star::linguistic2::XDictionary >& xDictionary ) throw (::com::sun::star::uno::RuntimeException); + virtual ::sal_Bool SAL_CALL removeDictionary( const ::com::sun::star::uno::Reference< ::com::sun::star::linguistic2::XDictionary >& xDictionary ) throw (::com::sun::star::uno::RuntimeException); + virtual ::sal_Bool SAL_CALL addDictionaryListEventListener( const ::com::sun::star::uno::Reference< ::com::sun::star::linguistic2::XDictionaryListEventListener >& xListener, ::sal_Bool bReceiveVerbose ) throw (::com::sun::star::uno::RuntimeException); + virtual ::sal_Bool SAL_CALL removeDictionaryListEventListener( const ::com::sun::star::uno::Reference< ::com::sun::star::linguistic2::XDictionaryListEventListener >& xListener ) throw (::com::sun::star::uno::RuntimeException); + virtual ::sal_Int16 SAL_CALL beginCollectEvents( ) throw (::com::sun::star::uno::RuntimeException); + virtual ::sal_Int16 SAL_CALL endCollectEvents( ) throw (::com::sun::star::uno::RuntimeException); + virtual ::sal_Int16 SAL_CALL flushEvents( ) throw (::com::sun::star::uno::RuntimeException); + virtual ::com::sun::star::uno::Reference< ::com::sun::star::linguistic2::XDictionary > SAL_CALL createDictionary( const ::rtl::OUString& aName, const ::com::sun::star::lang::Locale& aLocale, ::com::sun::star::linguistic2::DictionaryType eDicType, const ::rtl::OUString& aURL ) throw (::com::sun::star::uno::RuntimeException); + + // XSearchableDictionaryList + virtual ::com::sun::star::uno::Reference< ::com::sun::star::linguistic2::XDictionaryEntry > SAL_CALL queryDictionaryEntry( const ::rtl::OUString& aWord, const ::com::sun::star::lang::Locale& aLocale, sal_Bool bSearchPosDics, sal_Bool bSpellEntry ) throw(::com::sun::star::uno::RuntimeException); + + // XComponent + virtual void SAL_CALL dispose() throw(::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL addEventListener( const ::com::sun::star::uno::Reference< ::com::sun::star::lang::XEventListener >& xListener ) throw(::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL removeEventListener( const ::com::sun::star::uno::Reference< ::com::sun::star::lang::XEventListener >& aListener ) throw(::com::sun::star::uno::RuntimeException); + + // XServiceInfo + virtual ::rtl::OUString SAL_CALL getImplementationName() throw(::com::sun::star::uno::RuntimeException); + virtual sal_Bool SAL_CALL supportsService( const ::rtl::OUString& ServiceName ) throw(::com::sun::star::uno::RuntimeException); + virtual ::com::sun::star::uno::Sequence< ::rtl::OUString > SAL_CALL getSupportedServiceNames() throw(::com::sun::star::uno::RuntimeException); + + + static inline ::rtl::OUString getImplementationName_Static() throw(); + static com::sun::star::uno::Sequence< ::rtl::OUString > getSupportedServiceNames_Static() throw(); + + // non UNO-specific + void SaveDics(); +}; + +inline ::rtl::OUString DicList::getImplementationName_Static() throw() +{ + return A2OU( "com.sun.star.lingu2.DicList" ); +} + + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/linguistic/source/gciterator.cxx b/linguistic/source/gciterator.cxx new file mode 100644 index 000000000000..5939b1d97152 --- /dev/null +++ b/linguistic/source/gciterator.cxx @@ -0,0 +1,1189 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ +#include "precompiled_linguistic.hxx" + +#include <sal/macros.h> +#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 = SAL_N_ELEMENTS( aWhiteSpaces ); + +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 ); +} + + +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 &) + { + OSL_FAIL( "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) + { + GetConfiguredGCSvcs_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 && + aRes.nBehindEndOfSentencePosition != nSuggestedEnd + ) + { + 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(RTL_CONSTASCII_USTRINGPARAM("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()) + { + 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()) + { + 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()) + { + m_aEventListeners.addInterface( xListener ); + } +} + + +void SAL_CALL GrammarCheckingIterator::removeEventListener( + const uno::Reference< lang::XEventListener >& xListener ) +throw (uno::RuntimeException) +{ + if (xListener.is()) + { + 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 ---- + } +} + + + + +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; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/linguistic/source/gciterator.hxx b/linguistic/source/gciterator.hxx new file mode 100644 index 000000000000..aab04dd6f0ae --- /dev/null +++ b/linguistic/source/gciterator.hxx @@ -0,0 +1,201 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _LINGUISTIC_GRAMMARCHECKINGITERATOR_HXX_ +#define _LINGUISTIC_GRAMMARCHECKINGITERATOR_HXX_ + +#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/XEventListener.hpp> +#include <com/sun/star/linguistic2/XProofreadingIterator.hpp> +#include <com/sun/star/linguistic2/XLinguServiceEventListener.hpp> +#include <com/sun/star/linguistic2/XLinguServiceEventBroadcaster.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/util/XChangesBatch.hpp> + +#include <cppuhelper/implbase5.hxx> +#include <cppuhelper/weakref.hxx> +#include <osl/mutex.hxx> +#include <osl/conditn.hxx> +#include <rtl/instance.hxx> + +#include <map> +#include <deque> + +#include "defs.hxx" + + + +struct FPEntry +{ + // flat paragraph iterator + ::com::sun::star::uno::Reference< ::com::sun::star::text::XFlatParagraphIterator > m_xParaIterator; + + // flat paragraph + ::com::sun::star::uno::WeakReference< ::com::sun::star::text::XFlatParagraph > m_xPara; + + // document ID to identify different documents + ::rtl::OUString m_aDocId; + + // the starting position to be checked + sal_Int32 m_nStartIndex; + + // the flag to identify whether the document does automatical grammar checking + sal_Bool m_bAutomatic; + + FPEntry() + : m_aDocId() + , m_nStartIndex( 0 ) + , m_bAutomatic( 0 ) + { + } +}; + + + + +class GrammarCheckingIterator: + public cppu::WeakImplHelper5 + < + ::com::sun::star::linguistic2::XProofreadingIterator, + ::com::sun::star::linguistic2::XLinguServiceEventListener, + ::com::sun::star::linguistic2::XLinguServiceEventBroadcaster, + ::com::sun::star::lang::XComponent, + ::com::sun::star::lang::XServiceInfo + >, + public LinguDispatcher +{ + com::sun::star::uno::Reference< com::sun::star::lang::XMultiServiceFactory > m_xMSF; + + + //the queue is keeping track of all senteces to be checked + //every element of this queue is a FlatParagraphEntry struct-object + typedef std::deque< FPEntry > FPQueue_t; + + // queue for entries to be processed + FPQueue_t m_aFPEntriesQueue; + + // the flag to end the endless loop + sal_Bool m_bEnd; + + // Note that it must be the pointer and not the uno-reference to check if it is the same implementation object + typedef std::map< XComponent *, ::rtl::OUString > DocMap_t; + DocMap_t m_aDocIdMap; + + + // language -> implname mapping + typedef std::map< LanguageType, ::rtl::OUString > GCImplNames_t; + GCImplNames_t m_aGCImplNamesByLang; + + // implname -> UNO reference mapping + typedef std::map< ::rtl::OUString, ::com::sun::star::uno::Reference< ::com::sun::star::linguistic2::XProofreader > > GCReferences_t; + GCReferences_t m_aGCReferencesByService; + + ::rtl::OUString m_aCurCheckedDocId; + sal_Bool m_bGCServicesChecked; + sal_Int32 m_nDocIdCounter; + sal_Int32 m_nLastEndOfSentencePos; + osl::Condition m_aWakeUpThread; + osl::Condition m_aRequestEndThread; + + //! beware of initilization order ! + struct MyMutex : public rtl::Static< osl::Mutex, MyMutex > {}; + cppu::OInterfaceContainerHelper m_aEventListeners; + cppu::OInterfaceContainerHelper m_aNotifyListeners; + + ::com::sun::star::uno::Reference< ::com::sun::star::i18n::XBreakIterator > m_xBreakIterator; + mutable ::com::sun::star::uno::Reference< ::com::sun::star::util::XChangesBatch > m_xUpdateAccess; + + sal_Int32 NextDocId(); + ::rtl::OUString GetOrCreateDocId( const ::com::sun::star::uno::Reference< ::com::sun::star::lang::XComponent > &xComp ); + + void AddEntry( + ::com::sun::star::uno::WeakReference< ::com::sun::star::text::XFlatParagraphIterator > xFlatParaIterator, + ::com::sun::star::uno::WeakReference< ::com::sun::star::text::XFlatParagraph > xFlatPara, + const ::rtl::OUString &rDocId, sal_Int32 nStartIndex, sal_Bool bAutomatic ); + + void ProcessResult( const ::com::sun::star::linguistic2::ProofreadingResult &rRes, + const ::com::sun::star::uno::Reference< ::com::sun::star::text::XFlatParagraphIterator > &rxFlatParagraphIterator, + bool bIsAutomaticChecking ); + + sal_Int32 GetSuggestedEndOfSentence( const ::rtl::OUString &rText, sal_Int32 nSentenceStartPos, const ::com::sun::star::lang::Locale &rLocale ); + + void GetConfiguredGCSvcs_Impl(); + ::com::sun::star::uno::Reference< ::com::sun::star::linguistic2::XProofreader > GetGrammarChecker( const ::com::sun::star::lang::Locale & rLocale ); + + ::com::sun::star::uno::Reference< ::com::sun::star::util::XChangesBatch > GetUpdateAccess() const; + + // disallow use of copy c-tor and assignment operator + GrammarCheckingIterator( const GrammarCheckingIterator & ); + GrammarCheckingIterator & operator = ( const GrammarCheckingIterator & ); + +public: + + void DequeueAndCheck(); + + explicit GrammarCheckingIterator( const com::sun::star::uno::Reference< com::sun::star::lang::XMultiServiceFactory > & rxMgr ); + virtual ~GrammarCheckingIterator(); + + // XProofreadingIterator + virtual void SAL_CALL startProofreading( const ::com::sun::star::uno::Reference< ::com::sun::star::uno::XInterface >& xDocument, const ::com::sun::star::uno::Reference< ::com::sun::star::text::XFlatParagraphIteratorProvider >& xIteratorProvider ) throw (::com::sun::star::lang::IllegalArgumentException, ::com::sun::star::uno::RuntimeException); + virtual ::com::sun::star::linguistic2::ProofreadingResult SAL_CALL checkSentenceAtPosition( const ::com::sun::star::uno::Reference< ::com::sun::star::uno::XInterface >& xDocument, const ::com::sun::star::uno::Reference< ::com::sun::star::text::XFlatParagraph >& xFlatParagraph, const ::rtl::OUString& aText, const ::com::sun::star::lang::Locale& aLocale, ::sal_Int32 nStartOfSentencePosition, ::sal_Int32 nSuggestedBehindEndOfSentencePosition, ::sal_Int32 nErrorPositionInParagraph ) throw (::com::sun::star::lang::IllegalArgumentException, ::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL resetIgnoreRules( ) throw (::com::sun::star::uno::RuntimeException); + virtual ::sal_Bool SAL_CALL isProofreading( const ::com::sun::star::uno::Reference< ::com::sun::star::uno::XInterface >& xDocument ) throw (::com::sun::star::uno::RuntimeException); + + // XLinguServiceEventListener + virtual void SAL_CALL processLinguServiceEvent( const ::com::sun::star::linguistic2::LinguServiceEvent& aLngSvcEvent ) throw (::com::sun::star::uno::RuntimeException); + + // XLinguServiceEventBroadcaster + virtual ::sal_Bool SAL_CALL addLinguServiceEventListener( const ::com::sun::star::uno::Reference< ::com::sun::star::linguistic2::XLinguServiceEventListener >& xLstnr ) throw (::com::sun::star::uno::RuntimeException); + virtual ::sal_Bool SAL_CALL removeLinguServiceEventListener( const ::com::sun::star::uno::Reference< ::com::sun::star::linguistic2::XLinguServiceEventListener >& xLstnr ) throw (::com::sun::star::uno::RuntimeException); + + // XComponent + virtual void SAL_CALL dispose( ) throw (::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL addEventListener( const ::com::sun::star::uno::Reference< ::com::sun::star::lang::XEventListener >& xListener ) throw (::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL removeEventListener( const ::com::sun::star::uno::Reference< ::com::sun::star::lang::XEventListener >& aListener ) throw (::com::sun::star::uno::RuntimeException); + + // XEventListener + virtual void SAL_CALL disposing( const ::com::sun::star::lang::EventObject& Source ) throw (::com::sun::star::uno::RuntimeException); + + // XServiceInfo + virtual ::rtl::OUString SAL_CALL getImplementationName( ) throw (::com::sun::star::uno::RuntimeException); + virtual sal_Bool SAL_CALL supportsService( const ::rtl::OUString& ServiceName ) throw (::com::sun::star::uno::RuntimeException); + virtual ::com::sun::star::uno::Sequence< ::rtl::OUString > SAL_CALL getSupportedServiceNames( ) throw (::com::sun::star::uno::RuntimeException); + + // LinguDispatcher + virtual void SetServiceList( const ::com::sun::star::lang::Locale &rLocale, const ::com::sun::star::uno::Sequence< rtl::OUString > &rSvcImplNames ); + virtual ::com::sun::star::uno::Sequence< rtl::OUString > GetServiceList( const ::com::sun::star::lang::Locale &rLocale ) const; + virtual DspType GetDspType() const; +}; + + + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/linguistic/source/hhconvdic.cxx b/linguistic/source/hhconvdic.cxx new file mode 100644 index 000000000000..706005a68c62 --- /dev/null +++ b/linguistic/source/hhconvdic.cxx @@ -0,0 +1,167 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_linguistic.hxx" +#include <unicode/uscript.h> +#include <i18npool/lang.h> +#include <tools/urlobj.hxx> +#include <tools/debug.hxx> +#include <tools/fsys.hxx> +#include <tools/stream.hxx> +#include <tools/string.hxx> +#include <osl/mutex.hxx> +#include <unotools/processfactory.hxx> +#include <ucbhelper/content.hxx> + +#include <cppuhelper/factory.hxx> // helper for factories +#include <com/sun/star/linguistic2/XConversionDictionary.hpp> +#include <com/sun/star/linguistic2/ConversionDictionaryType.hpp> +#include <com/sun/star/lang/Locale.hpp> +#include <com/sun/star/uno/Reference.h> +#include <com/sun/star/registry/XRegistryKey.hpp> + +#include "hhconvdic.hxx" +#include "linguistic/misc.hxx" +#include "defs.hxx" + +using namespace utl; +using namespace osl; +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; + +using ::rtl::OUString; + +#define SN_HH_CONV_DICTIONARY "com.sun.star.linguistic2.HangulHanjaConversionDictionary" + + +#include <i18nutil/unicode.hxx> +#include <com/sun/star/i18n/UnicodeScript.hpp> + +using namespace i18n; + +#define SCRIPT_OTHERS 0 +#define SCRIPT_HANJA 1 +#define SCRIPT_HANGUL 2 + +// from i18npool/source/textconversion/textconversion_ko.cxx +sal_Int16 SAL_CALL checkScriptType(sal_Unicode c) throw (RuntimeException) +{ + UErrorCode status = U_ZERO_ERROR; + + UScriptCode scriptCode = uscript_getScript(c, &status); + + if ( !U_SUCCESS(status) ) throw RuntimeException(); + + return scriptCode == USCRIPT_HANGUL ? SCRIPT_HANGUL : + scriptCode == USCRIPT_HAN ? SCRIPT_HANJA : SCRIPT_OTHERS; +} + + + +sal_Bool TextIsAllScriptType( const OUString &rTxt, sal_Int16 nScriptType ) +{ + sal_Bool bIsAll = sal_True; + for (sal_Int32 i = 0; i < rTxt.getLength() && bIsAll; ++i) + { + if (checkScriptType( rTxt.getStr()[i]) != nScriptType) + bIsAll = sal_False; + } + return bIsAll; +} + + + +HHConvDic::HHConvDic( const String &rName, const String &rMainURL ) : + ConvDic( rName, LANGUAGE_KOREAN, ConversionDictionaryType::HANGUL_HANJA, sal_True, rMainURL ) +{ +} + + +HHConvDic::~HHConvDic() +{ +} + + +void SAL_CALL HHConvDic::addEntry( + const OUString& aLeftText, + const OUString& aRightText ) + throw (IllegalArgumentException, container::ElementExistException, RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + + if ((aLeftText.getLength() != aRightText.getLength()) || + !TextIsAllScriptType( aLeftText, SCRIPT_HANGUL ) || + !TextIsAllScriptType( aRightText, SCRIPT_HANJA )) + throw IllegalArgumentException(); + ConvDic::addEntry( aLeftText, aRightText ); +} + + +OUString SAL_CALL HHConvDic::getImplementationName( ) + throw (RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + return getImplementationName_Static(); +} + + +sal_Bool SAL_CALL HHConvDic::supportsService( const OUString& rServiceName ) + throw (RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + sal_Bool bRes = sal_False; + if (rServiceName.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM(SN_CONV_DICTIONARY)) || + rServiceName.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM(SN_HH_CONV_DICTIONARY))) + bRes = sal_True; + return bRes; +} + + +uno::Sequence< OUString > SAL_CALL HHConvDic::getSupportedServiceNames( ) + throw (RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + return getSupportedServiceNames_Static(); +} + + +uno::Sequence< OUString > HHConvDic::getSupportedServiceNames_Static() + throw() +{ + uno::Sequence< OUString > aSNS( 2 ); + aSNS.getArray()[0] = A2OU( SN_CONV_DICTIONARY ); + aSNS.getArray()[1] = A2OU( SN_HH_CONV_DICTIONARY ); + return aSNS; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/linguistic/source/hhconvdic.hxx b/linguistic/source/hhconvdic.hxx new file mode 100644 index 000000000000..1419e0d78608 --- /dev/null +++ b/linguistic/source/hhconvdic.hxx @@ -0,0 +1,78 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _LINGUISTIC_HHConvDic_HXX_ +#define _LINGUISTIC_HHConvDic_HXX_ + +#include <com/sun/star/linguistic2/XConversionDictionary.hpp> +#include <com/sun/star/util/XFlushable.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <cppuhelper/implbase3.hxx> +#include <cppuhelper/interfacecontainer.h> +#include <tools/string.hxx> + +#include "linguistic/misc.hxx" +#include "defs.hxx" +#include "convdic.hxx" + + +class HHConvDic : + public ConvDic +{ + // disallow copy-constructor and assignment-operator for now + HHConvDic(const HHConvDic &); + HHConvDic & operator = (const HHConvDic &); + +public: + HHConvDic( const String &rName, const String &rMainURL ); + virtual ~HHConvDic(); + + // XConversionDictionary + virtual void SAL_CALL addEntry( const ::rtl::OUString& aLeftText, const ::rtl::OUString& aRightText ) throw (::com::sun::star::lang::IllegalArgumentException, ::com::sun::star::container::ElementExistException, ::com::sun::star::uno::RuntimeException); + + // XServiceInfo + virtual ::rtl::OUString SAL_CALL getImplementationName( ) throw (::com::sun::star::uno::RuntimeException); + virtual sal_Bool SAL_CALL supportsService( const ::rtl::OUString& ServiceName ) throw (::com::sun::star::uno::RuntimeException); + virtual ::com::sun::star::uno::Sequence< ::rtl::OUString > SAL_CALL getSupportedServiceNames( ) throw (::com::sun::star::uno::RuntimeException); + + + static inline ::rtl::OUString + getImplementationName_Static() throw(); + static com::sun::star::uno::Sequence< ::rtl::OUString > + getSupportedServiceNames_Static() throw(); +}; + +inline ::rtl::OUString HHConvDic::getImplementationName_Static() throw() +{ + return A2OU( "com.sun.star.lingu2.HHConvDic" ); +} + + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/linguistic/source/hyphdsp.cxx b/linguistic/source/hyphdsp.cxx new file mode 100644 index 000000000000..d745b10f4d4e --- /dev/null +++ b/linguistic/source/hyphdsp.cxx @@ -0,0 +1,724 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_linguistic.hxx" + + +#include <cppuhelper/factory.hxx> // helper for factories +#include <com/sun/star/registry/XRegistryKey.hpp> +#include <com/sun/star/linguistic2/XSearchableDictionaryList.hpp> +#include <com/sun/star/linguistic2/XHyphenatedWord.hpp> +#include <rtl/ustrbuf.hxx> +#include <i18npool/lang.h> +#include <unotools/localedatawrapper.hxx> +#include <tools/debug.hxx> +#include <svl/lngmisc.hxx> +#include <unotools/processfactory.hxx> +#include <osl/mutex.hxx> + +#include "hyphdsp.hxx" +#include "linguistic/hyphdta.hxx" +#include "linguistic/lngprops.hxx" +#include "lngsvcmgr.hxx" + + +using namespace utl; +using namespace osl; +using namespace com::sun::star; +using namespace com::sun::star::beans; +using namespace com::sun::star::lang; +using namespace com::sun::star::uno; +using namespace com::sun::star::linguistic2; +using namespace linguistic; + +using ::rtl::OUString; +using ::rtl::OUStringBuffer; + + +HyphenatorDispatcher::HyphenatorDispatcher( LngSvcMgr &rLngSvcMgr ) : + rMgr (rLngSvcMgr) +{ +} + + +HyphenatorDispatcher::~HyphenatorDispatcher() +{ + ClearSvcList(); +} + + +void HyphenatorDispatcher::ClearSvcList() +{ + // release memory for each table entry + HyphSvcByLangMap_t aTmp; + aSvcMap.swap( aTmp ); +} + + +Reference<XHyphenatedWord> HyphenatorDispatcher::buildHyphWord( + const OUString rOrigWord, + const Reference<XDictionaryEntry> &xEntry, + sal_Int16 nLang, sal_Int16 nMaxLeading ) +{ + MutexGuard aGuard( GetLinguMutex() ); + + Reference< XHyphenatedWord > xRes; + + if (xEntry.is()) + { + OUString aText( xEntry->getDictionaryWord() ); + sal_Int32 nTextLen = aText.getLength(); + + // trailing '=' means "hyphenation should not be possible" + if (nTextLen > 0 && aText[ nTextLen - 1 ] != '=') + { + sal_Int16 nHyphenationPos = -1; + + OUStringBuffer aTmp( nTextLen ); + sal_Bool bSkip = sal_False; + sal_Int32 nHyphIdx = -1; + sal_Int32 nLeading = 0; + for (sal_Int32 i = 0; i < nTextLen; i++) + { + sal_Unicode cTmp = aText[i]; + if (cTmp != '=') + { + aTmp.append( cTmp ); + nLeading++; + bSkip = sal_False; + nHyphIdx++; + } + else + { + if (!bSkip && nHyphIdx >= 0) + { + if (nLeading <= nMaxLeading) + nHyphenationPos = (sal_Int16) nHyphIdx; + } + bSkip = sal_True; //! multiple '=' should count as one only + } + } + + if (nHyphenationPos > 0) + { + aText = aTmp.makeStringAndClear(); + +#if OSL_DEBUG_LEVEL > 1 + { + if (aText != rOrigWord) + { + // both words should only differ by a having a trailing '.' + // character or not... + OUString aShorter, aLonger; + if (aText.getLength() <= rOrigWord.getLength()) + { + aShorter = aText; + aLonger = rOrigWord; + } + else + { + aShorter = rOrigWord; + aLonger = aText; + } + xub_StrLen nS = sal::static_int_cast< xub_StrLen >( aShorter.getLength() ); + xub_StrLen nL = sal::static_int_cast< xub_StrLen >( aLonger.getLength() ); + if (nS > 0 && nL > 0) + { + DBG_ASSERT( (nS + 1 == nL) && aLonger[nL-1] == (sal_Unicode) '.', + "HyphenatorDispatcher::buildHyphWord: unexpected difference between words!" ); + } + } + } +#endif + //! take care of #i22591# + aText = rOrigWord; + + DBG_ASSERT( aText == rOrigWord, "failed to " ); + xRes = new HyphenatedWord( aText, nLang, nHyphenationPos, + aText, nHyphenationPos ); + } + } + } + + return xRes; +} + + +Reference< XPossibleHyphens > HyphenatorDispatcher::buildPossHyphens( + const Reference< XDictionaryEntry > &xEntry, sal_Int16 nLanguage ) +{ + MutexGuard aGuard( GetLinguMutex() ); + + Reference<XPossibleHyphens> xRes; + + if (xEntry.is()) + { + // text with hyphenation info + OUString aText( xEntry->getDictionaryWord() ); + sal_Int32 nTextLen = aText.getLength(); + + // trailing '=' means "hyphenation should not be possible" + if (nTextLen > 0 && aText[ nTextLen - 1 ] != '=') + { + // sequence to hold hyphenation positions + Sequence< sal_Int16 > aHyphPos( nTextLen ); + sal_Int16 *pPos = aHyphPos.getArray(); + sal_Int32 nHyphCount = 0; + + OUStringBuffer aTmp( nTextLen ); + sal_Bool bSkip = sal_False; + sal_Int32 nHyphIdx = -1; + for (sal_Int32 i = 0; i < nTextLen; i++) + { + sal_Unicode cTmp = aText[i]; + if (cTmp != '=') + { + aTmp.append( cTmp ); + bSkip = sal_False; + nHyphIdx++; + } + else + { + if (!bSkip && nHyphIdx >= 0) + pPos[ nHyphCount++ ] = (sal_Int16) nHyphIdx; + bSkip = sal_True; //! multiple '=' should count as one only + } + } + + // ignore (multiple) trailing '=' + if (bSkip && nHyphIdx >= 0) + { + nHyphCount--; + } + DBG_ASSERT( nHyphCount >= 0, "lng : invalid hyphenation count"); + + if (nHyphCount > 0) + { + aHyphPos.realloc( nHyphCount ); + xRes = new PossibleHyphens( aTmp.makeStringAndClear(), nLanguage, + aText, aHyphPos ); + } + } + } + + return xRes; +} + + +Sequence< Locale > SAL_CALL HyphenatorDispatcher::getLocales() + throw(RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + + Sequence< Locale > aLocales( static_cast< sal_Int32 >(aSvcMap.size()) ); + Locale *pLocales = aLocales.getArray(); + HyphSvcByLangMap_t::const_iterator aIt; + for (aIt = aSvcMap.begin(); aIt != aSvcMap.end(); ++aIt) + { + *pLocales++ = CreateLocale( aIt->first ); + } + return aLocales; +} + + +sal_Bool SAL_CALL HyphenatorDispatcher::hasLocale(const Locale& rLocale) + throw(RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + HyphSvcByLangMap_t::const_iterator aIt( aSvcMap.find( LocaleToLanguage( rLocale ) ) ); + return aIt != aSvcMap.end(); +} + + +Reference< XHyphenatedWord > SAL_CALL + HyphenatorDispatcher::hyphenate( + const OUString& rWord, const Locale& rLocale, sal_Int16 nMaxLeading, + const PropertyValues& rProperties ) + throw(IllegalArgumentException, RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + + Reference< XHyphenatedWord > xRes; + + sal_Int32 nWordLen = rWord.getLength(); + sal_Int16 nLanguage = LocaleToLanguage( rLocale ); + if (nLanguage == LANGUAGE_NONE || !nWordLen || + nMaxLeading == 0 || nMaxLeading == nWordLen) + return xRes; + + // search for entry with that language + HyphSvcByLangMap_t::iterator aIt( aSvcMap.find( nLanguage ) ); + LangSvcEntries_Hyph *pEntry = aIt != aSvcMap.end() ? aIt->second.get() : NULL; + + sal_Bool bWordModified = sal_False; + if (!pEntry || (nMaxLeading < 0 || nMaxLeading > nWordLen)) + { +#ifdef LINGU_EXCEPTIONS + throw IllegalArgumentException(); +#else + return NULL; +#endif + } + else + { + OUString aChkWord( rWord ); + + // replace typographical apostroph by ascii apostroph + String aSingleQuote( GetLocaleDataWrapper( nLanguage ).getQuotationMarkEnd() ); + DBG_ASSERT( 1 == aSingleQuote.Len(), "unexpectend length of quotation mark" ); + if (aSingleQuote.Len()) + aChkWord = aChkWord.replace( aSingleQuote.GetChar(0), '\'' ); + + bWordModified |= RemoveHyphens( aChkWord ); + if (IsIgnoreControlChars( rProperties, GetPropSet() )) + bWordModified |= RemoveControlChars( aChkWord ); + sal_Int16 nChkMaxLeading = (sal_Int16) GetPosInWordToCheck( rWord, nMaxLeading ); + + // check for results from (positive) dictionaries which have precedence! + Reference< XDictionaryEntry > xEntry; + + if (GetDicList().is() && IsUseDicList( rProperties, GetPropSet() )) + { + xEntry = GetDicList()->queryDictionaryEntry( aChkWord, rLocale, + sal_True, sal_False ); + } + + if (xEntry.is()) + { + //! because queryDictionaryEntry (in the end DictionaryNeo::getEntry) + //! does not distinguish betwee "XYZ" and "XYZ." in order to avoid + //! to require them as different entry we have to supply the + //! original word here as well so it can be used in th result + //! otherwise a strange effect may occur (see #i22591#) + xRes = buildHyphWord( rWord, xEntry, nLanguage, nChkMaxLeading ); + } + else + { + sal_Int32 nLen = pEntry->aSvcImplNames.getLength() > 0 ? 1 : 0; + DBG_ASSERT( pEntry->nLastTriedSvcIndex < nLen, + "lng : index out of range"); + + sal_Int32 i = 0; + Reference< XHyphenator > xHyph; + if (pEntry->aSvcRefs.getLength() > 0) + xHyph = pEntry->aSvcRefs[0]; + + // try already instantiated service + if (i <= pEntry->nLastTriedSvcIndex) + { + if (xHyph.is() && xHyph->hasLocale( rLocale )) + xRes = xHyph->hyphenate( aChkWord, rLocale, nChkMaxLeading, + rProperties ); + ++i; + } + else if (pEntry->nLastTriedSvcIndex < nLen - 1) + // instantiate services and try it + { + Reference< XHyphenator > *pRef = pEntry->aSvcRefs.getArray(); + + Reference< XMultiServiceFactory > xMgr( getProcessServiceFactory() ); + if (xMgr.is()) + { + // build service initialization argument + Sequence< Any > aArgs(2); + aArgs.getArray()[0] <<= GetPropSet(); + + // create specific service via it's implementation name + try + { + xHyph = Reference< XHyphenator >( + xMgr->createInstanceWithArguments( + pEntry->aSvcImplNames[0], aArgs ), UNO_QUERY ); + } + catch (uno::Exception &) + { + DBG_ASSERT( 0, "createInstanceWithArguments failed" ); + } + pRef [i] = xHyph; + + Reference< XLinguServiceEventBroadcaster > + xBroadcaster( xHyph, UNO_QUERY ); + if (xBroadcaster.is()) + rMgr.AddLngSvcEvtBroadcaster( xBroadcaster ); + + if (xHyph.is() && xHyph->hasLocale( rLocale )) + xRes = xHyph->hyphenate( aChkWord, rLocale, nChkMaxLeading, + rProperties ); + + pEntry->nLastTriedSvcIndex = (sal_Int16) i; + ++i; + + // if language is not supported by the services + // remove it from the list. + if (xHyph.is() && !xHyph->hasLocale( rLocale )) + aSvcMap.erase( nLanguage ); + } + } + } // if (xEntry.is()) + } + + if (bWordModified && xRes.is()) + xRes = RebuildHyphensAndControlChars( rWord, xRes ); + + if (xRes.is() && xRes->getWord() != rWord) + { + xRes = new HyphenatedWord( rWord, nLanguage, xRes->getHyphenationPos(), + xRes->getHyphenatedWord(), + xRes->getHyphenPos() ); + } + + return xRes; +} + + +Reference< XHyphenatedWord > SAL_CALL + HyphenatorDispatcher::queryAlternativeSpelling( + const OUString& rWord, const Locale& rLocale, sal_Int16 nIndex, + const PropertyValues& rProperties ) + throw(IllegalArgumentException, RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + + Reference< XHyphenatedWord > xRes; + + sal_Int32 nWordLen = rWord.getLength(); + sal_Int16 nLanguage = LocaleToLanguage( rLocale ); + if (nLanguage == LANGUAGE_NONE || !nWordLen) + return xRes; + + // search for entry with that language + HyphSvcByLangMap_t::iterator aIt( aSvcMap.find( nLanguage ) ); + LangSvcEntries_Hyph *pEntry = aIt != aSvcMap.end() ? aIt->second.get() : NULL; + + sal_Bool bWordModified = sal_False; + if (!pEntry || !(0 <= nIndex && nIndex <= nWordLen - 2)) + { +#ifdef LINGU_EXCEPTIONS + throw IllegalArgumentException(); +#else + return NULL; +#endif + } + else + { + OUString aChkWord( rWord ); + + // replace typographical apostroph by ascii apostroph + String aSingleQuote( GetLocaleDataWrapper( nLanguage ).getQuotationMarkEnd() ); + DBG_ASSERT( 1 == aSingleQuote.Len(), "unexpectend length of quotation mark" ); + if (aSingleQuote.Len()) + aChkWord = aChkWord.replace( aSingleQuote.GetChar(0), '\'' ); + + bWordModified |= RemoveHyphens( aChkWord ); + if (IsIgnoreControlChars( rProperties, GetPropSet() )) + bWordModified |= RemoveControlChars( aChkWord ); + sal_Int16 nChkIndex = (sal_Int16) GetPosInWordToCheck( rWord, nIndex ); + + // check for results from (positive) dictionaries which have precedence! + Reference< XDictionaryEntry > xEntry; + + if (GetDicList().is() && IsUseDicList( rProperties, GetPropSet() )) + { + xEntry = GetDicList()->queryDictionaryEntry( aChkWord, rLocale, + sal_True, sal_False ); + } + + if (xEntry.is()) + { + //! alternative spellings not yet supported by dictionaries + } + else + { + sal_Int32 nLen = pEntry->aSvcImplNames.getLength() > 0 ? 1 : 0; + DBG_ASSERT( pEntry->nLastTriedSvcIndex < nLen, + "lng : index out of range"); + + sal_Int32 i = 0; + Reference< XHyphenator > xHyph; + if (pEntry->aSvcRefs.getLength() > 0) + xHyph = pEntry->aSvcRefs[0]; + + // try already instantiated service + if (i <= pEntry->nLastTriedSvcIndex) + { + if (xHyph.is() && xHyph->hasLocale( rLocale )) + xRes = xHyph->queryAlternativeSpelling( aChkWord, rLocale, + nChkIndex, rProperties ); + ++i; + } + else if (pEntry->nLastTriedSvcIndex < nLen - 1) + // instantiate services and try it + { + Reference< XHyphenator > *pRef = pEntry->aSvcRefs.getArray(); + + Reference< XMultiServiceFactory > xMgr( getProcessServiceFactory() ); + if (xMgr.is()) + { + // build service initialization argument + Sequence< Any > aArgs(2); + aArgs.getArray()[0] <<= GetPropSet(); + + // create specific service via it's implementation name + try + { + xHyph = Reference< XHyphenator >( + xMgr->createInstanceWithArguments( + pEntry->aSvcImplNames[0], aArgs ), UNO_QUERY ); + } + catch (uno::Exception &) + { + DBG_ASSERT( 0, "createInstanceWithArguments failed" ); + } + pRef [i] = xHyph; + + Reference< XLinguServiceEventBroadcaster > + xBroadcaster( xHyph, UNO_QUERY ); + if (xBroadcaster.is()) + rMgr.AddLngSvcEvtBroadcaster( xBroadcaster ); + + if (xHyph.is() && xHyph->hasLocale( rLocale )) + xRes = xHyph->queryAlternativeSpelling( aChkWord, rLocale, + nChkIndex, rProperties ); + + pEntry->nLastTriedSvcIndex = (sal_Int16) i; + ++i; + + // if language is not supported by the services + // remove it from the list. + if (xHyph.is() && !xHyph->hasLocale( rLocale )) + aSvcMap.erase( nLanguage ); + } + } + } // if (xEntry.is()) + } + + if (bWordModified && xRes.is()) + xRes = RebuildHyphensAndControlChars( rWord, xRes ); + + if (xRes.is() && xRes->getWord() != rWord) + { + xRes = new HyphenatedWord( rWord, nLanguage, xRes->getHyphenationPos(), + xRes->getHyphenatedWord(), + xRes->getHyphenPos() ); + } + + return xRes; +} + + +Reference< XPossibleHyphens > SAL_CALL + HyphenatorDispatcher::createPossibleHyphens( + const OUString& rWord, const Locale& rLocale, + const PropertyValues& rProperties ) + throw(IllegalArgumentException, RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + + Reference< XPossibleHyphens > xRes; + + sal_Int16 nLanguage = LocaleToLanguage( rLocale ); + if (nLanguage == LANGUAGE_NONE || !rWord.getLength()) + return xRes; + + // search for entry with that language + HyphSvcByLangMap_t::iterator aIt( aSvcMap.find( nLanguage ) ); + LangSvcEntries_Hyph *pEntry = aIt != aSvcMap.end() ? aIt->second.get() : NULL; + + if (!pEntry) + { +#ifdef LINGU_EXCEPTIONS + throw IllegalArgumentException(); +#endif + } + else + { + OUString aChkWord( rWord ); + + // replace typographical apostroph by ascii apostroph + String aSingleQuote( GetLocaleDataWrapper( nLanguage ).getQuotationMarkEnd() ); + DBG_ASSERT( 1 == aSingleQuote.Len(), "unexpectend length of quotation mark" ); + if (aSingleQuote.Len()) + aChkWord = aChkWord.replace( aSingleQuote.GetChar(0), '\'' ); + + RemoveHyphens( aChkWord ); + if (IsIgnoreControlChars( rProperties, GetPropSet() )) + RemoveControlChars( aChkWord ); + + // check for results from (positive) dictionaries which have precedence! + Reference< XDictionaryEntry > xEntry; + + if (GetDicList().is() && IsUseDicList( rProperties, GetPropSet() )) + { + xEntry = GetDicList()->queryDictionaryEntry( aChkWord, rLocale, + sal_True, sal_False ); + } + + if (xEntry.is()) + { + xRes = buildPossHyphens( xEntry, nLanguage ); + } + else + { + sal_Int32 nLen = pEntry->aSvcImplNames.getLength() > 0 ? 1 : 0; + DBG_ASSERT( pEntry->nLastTriedSvcIndex < nLen, + "lng : index out of range"); + + sal_Int32 i = 0; + Reference< XHyphenator > xHyph; + if (pEntry->aSvcRefs.getLength() > 0) + xHyph = pEntry->aSvcRefs[0]; + + // try already instantiated service + if (i <= pEntry->nLastTriedSvcIndex) + { + if (xHyph.is() && xHyph->hasLocale( rLocale )) + xRes = xHyph->createPossibleHyphens( aChkWord, rLocale, + rProperties ); + ++i; + } + else if (pEntry->nLastTriedSvcIndex < nLen - 1) + // instantiate services and try it + { + Reference< XHyphenator > *pRef = pEntry->aSvcRefs.getArray(); + + Reference< XMultiServiceFactory > xMgr( getProcessServiceFactory() ); + if (xMgr.is()) + { + // build service initialization argument + Sequence< Any > aArgs(2); + aArgs.getArray()[0] <<= GetPropSet(); + + // create specific service via it's implementation name + try + { + xHyph = Reference< XHyphenator >( + xMgr->createInstanceWithArguments( + pEntry->aSvcImplNames[0], aArgs ), UNO_QUERY ); + } + catch (uno::Exception &) + { + DBG_ASSERT( 0, "createWithArguments failed" ); + } + pRef [i] = xHyph; + + Reference< XLinguServiceEventBroadcaster > + xBroadcaster( xHyph, UNO_QUERY ); + if (xBroadcaster.is()) + rMgr.AddLngSvcEvtBroadcaster( xBroadcaster ); + + if (xHyph.is() && xHyph->hasLocale( rLocale )) + xRes = xHyph->createPossibleHyphens( aChkWord, rLocale, + rProperties ); + + pEntry->nLastTriedSvcIndex = (sal_Int16) i; + ++i; + + // if language is not supported by the services + // remove it from the list. + if (xHyph.is() && !xHyph->hasLocale( rLocale )) + aSvcMap.erase( nLanguage ); + } + } + } // if (xEntry.is()) + } + + if (xRes.is() && xRes->getWord() != rWord) + { + xRes = new PossibleHyphens( rWord, nLanguage, + xRes->getPossibleHyphens(), + xRes->getHyphenationPositions() ); + } + + return xRes; +} + + +void HyphenatorDispatcher::SetServiceList( const Locale &rLocale, + const Sequence< OUString > &rSvcImplNames ) +{ + MutexGuard aGuard( GetLinguMutex() ); + + sal_Int16 nLanguage = LocaleToLanguage( rLocale ); + + sal_Int32 nLen = rSvcImplNames.getLength(); + if (0 == nLen) + // remove entry + aSvcMap.erase( nLanguage ); + else + { + // modify/add entry + LangSvcEntries_Hyph *pEntry = aSvcMap[ nLanguage ].get(); + if (pEntry) + { + pEntry->Clear(); + pEntry->aSvcImplNames = rSvcImplNames; + pEntry->aSvcImplNames.realloc(1); + pEntry->aSvcRefs = Sequence< Reference < XHyphenator > > ( 1 ); + } + else + { + boost::shared_ptr< LangSvcEntries_Hyph > pTmpEntry( new LangSvcEntries_Hyph( rSvcImplNames[0] ) ); + pTmpEntry->aSvcRefs = Sequence< Reference < XHyphenator > >( 1 ); + aSvcMap[ nLanguage ] = pTmpEntry; + } + } +} + + +Sequence< OUString > + HyphenatorDispatcher::GetServiceList( const Locale &rLocale ) const +{ + MutexGuard aGuard( GetLinguMutex() ); + + Sequence< OUString > aRes; + + // search for entry with that language and use data from that + sal_Int16 nLanguage = LocaleToLanguage( rLocale ); + HyphenatorDispatcher *pThis = (HyphenatorDispatcher *) this; + const HyphSvcByLangMap_t::iterator aIt( pThis->aSvcMap.find( nLanguage ) ); + const LangSvcEntries_Hyph *pEntry = aIt != aSvcMap.end() ? aIt->second.get() : NULL; + if (pEntry) + { + aRes = pEntry->aSvcImplNames; + if (aRes.getLength() > 0) + aRes.realloc(1); + } + + return aRes; +} + + +LinguDispatcher::DspType HyphenatorDispatcher::GetDspType() const +{ + return DSP_HYPH; +} + + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/linguistic/source/hyphdsp.hxx b/linguistic/source/hyphdsp.hxx new file mode 100644 index 000000000000..520365d0edcf --- /dev/null +++ b/linguistic/source/hyphdsp.hxx @@ -0,0 +1,172 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _LINGUISTIC_HYPHDSP_HXX_ +#define _LINGUISTIC_HYPHDSP_HXX_ + + +#include <com/sun/star/lang/XComponent.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/lang/XServiceDisplayName.hpp> +#include <com/sun/star/linguistic2/XHyphenator.hpp> +#include <com/sun/star/linguistic2/XPossibleHyphens.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/linguistic2/XSearchableDictionaryList.hpp> +#include <com/sun/star/linguistic2/XLinguServiceEventBroadcaster.hpp> + +#include <uno/lbnames.h> // CPPU_CURRENT_LANGUAGE_BINDING_NAME macro, which specify the environment type +#include <cppuhelper/implbase1.hxx> // helper for implementations + +#include <boost/shared_ptr.hpp> +#include <map> + +#include "lngopt.hxx" +#include "linguistic/misc.hxx" +#include "defs.hxx" + +class LngSvcMgr; + + +class HyphenatorDispatcher : + public cppu::WeakImplHelper1 + < + ::com::sun::star::linguistic2::XHyphenator + >, + public LinguDispatcher +{ + typedef boost::shared_ptr< LangSvcEntries_Hyph > LangSvcEntries_Hyph_Ptr_t; + typedef std::map< LanguageType, LangSvcEntries_Hyph_Ptr_t > HyphSvcByLangMap_t; + HyphSvcByLangMap_t aSvcMap; + + ::com::sun::star::uno::Reference< + ::com::sun::star::beans::XPropertySet > xPropSet; + ::com::sun::star::uno::Reference< + ::com::sun::star::linguistic2::XSearchableDictionaryList > xDicList; + + LngSvcMgr &rMgr; + + // disallow copy-constructor and assignment-operator for now + HyphenatorDispatcher(const HyphenatorDispatcher &); + HyphenatorDispatcher & operator = (const HyphenatorDispatcher &); + + inline ::com::sun::star::uno::Reference< + ::com::sun::star::beans::XPropertySet > + GetPropSet(); + inline ::com::sun::star::uno::Reference< + ::com::sun::star::linguistic2::XSearchableDictionaryList > + GetDicList(); + + void ClearSvcList(); + + com::sun::star::uno::Reference< + ::com::sun::star::linguistic2::XHyphenatedWord> + buildHyphWord( const rtl::OUString rOrigWord, + const ::com::sun::star::uno::Reference< + ::com::sun::star::linguistic2::XDictionaryEntry> &xEntry, + sal_Int16 nLang, sal_Int16 nMaxLeading ); + + com::sun::star::uno::Reference< + ::com::sun::star::linguistic2::XPossibleHyphens > + buildPossHyphens( const ::com::sun::star::uno::Reference< + ::com::sun::star::linguistic2::XDictionaryEntry > &xEntry, + sal_Int16 nLanguage ); + +public: + HyphenatorDispatcher( LngSvcMgr &rLngSvcMgr ); + virtual ~HyphenatorDispatcher(); + + // XSupportedLocales + virtual ::com::sun::star::uno::Sequence< + ::com::sun::star::lang::Locale > SAL_CALL + getLocales() + throw(::com::sun::star::uno::RuntimeException); + virtual sal_Bool SAL_CALL + hasLocale( const ::com::sun::star::lang::Locale& aLocale ) + throw(::com::sun::star::uno::RuntimeException); + + // XHyphenator + virtual ::com::sun::star::uno::Reference< + ::com::sun::star::linguistic2::XHyphenatedWord > SAL_CALL + hyphenate( const ::rtl::OUString& aWord, + const ::com::sun::star::lang::Locale& aLocale, + sal_Int16 nMaxLeading, + const ::com::sun::star::beans::PropertyValues& aProperties ) + throw(::com::sun::star::lang::IllegalArgumentException, + ::com::sun::star::uno::RuntimeException); + virtual ::com::sun::star::uno::Reference< + ::com::sun::star::linguistic2::XHyphenatedWord > SAL_CALL + queryAlternativeSpelling( const ::rtl::OUString& aWord, + const ::com::sun::star::lang::Locale& aLocale, + sal_Int16 nIndex, + const ::com::sun::star::beans::PropertyValues& aProperties ) + throw(::com::sun::star::lang::IllegalArgumentException, + ::com::sun::star::uno::RuntimeException); + virtual ::com::sun::star::uno::Reference< + ::com::sun::star::linguistic2::XPossibleHyphens > SAL_CALL + createPossibleHyphens( + const ::rtl::OUString& aWord, + const ::com::sun::star::lang::Locale& aLocale, + const ::com::sun::star::beans::PropertyValues& aProperties ) + throw(::com::sun::star::lang::IllegalArgumentException, + ::com::sun::star::uno::RuntimeException); + + // LinguDispatcher + virtual void + SetServiceList( const ::com::sun::star::lang::Locale &rLocale, + const ::com::sun::star::uno::Sequence< + rtl::OUString > &rSvcImplNames ); + virtual ::com::sun::star::uno::Sequence< rtl::OUString > + GetServiceList( const ::com::sun::star::lang::Locale &rLocale ) const; + virtual DspType + GetDspType() const; +}; + + +inline ::com::sun::star::uno::Reference< + ::com::sun::star::beans::XPropertySet > + HyphenatorDispatcher::GetPropSet() +{ + return xPropSet.is() ? + xPropSet : xPropSet = ::linguistic::GetLinguProperties(); +} + + +inline ::com::sun::star::uno::Reference< + ::com::sun::star::linguistic2::XSearchableDictionaryList > + HyphenatorDispatcher::GetDicList() +{ + return xDicList.is() ? + xDicList : xDicList = ::linguistic::GetSearchableDictionaryList(); +} + + + + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/linguistic/source/hyphdta.cxx b/linguistic/source/hyphdta.cxx new file mode 100644 index 000000000000..12244e21fc57 --- /dev/null +++ b/linguistic/source/hyphdta.cxx @@ -0,0 +1,203 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_linguistic.hxx" + +#include "linguistic/hyphdta.hxx" +#include "linguistic/lngprops.hxx" +#include "linguistic/misc.hxx" +#include <osl/mutex.hxx> + + +#include <rtl/ustrbuf.hxx> +#include <tools/debug.hxx> +#include <svl/lngmisc.hxx> +#include <unotools/localedatawrapper.hxx> + +using namespace osl; +using namespace com::sun::star; +using namespace com::sun::star::lang; +using namespace com::sun::star::uno; +using namespace com::sun::star::linguistic2; + +using ::rtl::OUString; + +namespace linguistic +{ + + +HyphenatedWord::HyphenatedWord(const OUString &rWord, sal_Int16 nLang, sal_Int16 nHPos, + const OUString &rHyphWord, sal_Int16 nPos ) : + aWord (rWord), + aHyphenatedWord (rHyphWord), + nHyphPos (nPos), + nHyphenationPos (nHPos), + nLanguage (nLang) +{ + String aSingleQuote( GetLocaleDataWrapper( nLanguage ).getQuotationMarkEnd() ); + DBG_ASSERT( 1 == aSingleQuote.Len(), "unexpectend length of quotation mark" ); + if (aSingleQuote.Len()) + { + // ignore typographical apostrophes (which got replaced in original + // word when being checked for hyphenation) in results. + OUString aTmpWord( rWord ); + OUString aTmpHyphWord( rHyphWord ); + aTmpWord = aTmpWord .replace( aSingleQuote.GetChar(0), '\'' ); + aTmpHyphWord = aTmpHyphWord.replace( aSingleQuote.GetChar(0), '\'' ); + bIsAltSpelling = aTmpWord != aTmpHyphWord; + } + else + bIsAltSpelling = rWord != rHyphWord; +} + + +HyphenatedWord::~HyphenatedWord() +{ +} + + +OUString SAL_CALL HyphenatedWord::getWord() + throw(RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + return aWord; +} + + +Locale SAL_CALL HyphenatedWord::getLocale() + throw(RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + + Locale aRes; + return LanguageToLocale( aRes, nLanguage ); +} + + +sal_Int16 SAL_CALL HyphenatedWord::getHyphenationPos() + throw(RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + return nHyphenationPos; +} + + +OUString SAL_CALL HyphenatedWord::getHyphenatedWord() + throw(RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + return aHyphenatedWord; +} + + +sal_Int16 SAL_CALL HyphenatedWord::getHyphenPos() + throw(RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + return nHyphPos; +} + + +sal_Bool SAL_CALL HyphenatedWord::isAlternativeSpelling() + throw(RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + return bIsAltSpelling; +} + + + + +PossibleHyphens::PossibleHyphens(const OUString &rWord, sal_Int16 nLang, + const OUString &rHyphWord, + const Sequence< sal_Int16 > &rPositions) : + aWord (rWord), + aWordWithHyphens(rHyphWord), + aOrigHyphenPos (rPositions), + nLanguage (nLang) +{ +} + + +PossibleHyphens::~PossibleHyphens() +{ +} + + +OUString SAL_CALL PossibleHyphens::getWord() + throw(RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + return aWord; +} + + +Locale SAL_CALL PossibleHyphens::getLocale() + throw(RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + return CreateLocale( nLanguage ); +} + + +OUString SAL_CALL PossibleHyphens::getPossibleHyphens() + throw(RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + return aWordWithHyphens; +} + + +Sequence< sal_Int16 > SAL_CALL PossibleHyphens::getHyphenationPositions() + throw(RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + return aOrigHyphenPos; +} + +com::sun::star::uno::Reference <com::sun::star::linguistic2::XHyphenatedWord> HyphenatedWord::CreateHyphenatedWord( + const ::rtl::OUString &rWord, sal_Int16 nLang, sal_Int16 nHyphenationPos, + const ::rtl::OUString &rHyphenatedWord, sal_Int16 nHyphenPos ) +{ + return new HyphenatedWord( rWord, nLang, nHyphenationPos, rHyphenatedWord, nHyphenPos ); +} + +com::sun::star::uno::Reference < com::sun::star::linguistic2::XPossibleHyphens > PossibleHyphens::CreatePossibleHyphens + (const ::rtl::OUString &rWord, sal_Int16 nLang, + const ::rtl::OUString &rHyphWord, + const ::com::sun::star::uno::Sequence< sal_Int16 > &rPositions) +{ + return new PossibleHyphens( rWord, nLang, rHyphWord, rPositions ); +} + + + +} // namespace linguistic + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/linguistic/source/iprcache.cxx b/linguistic/source/iprcache.cxx new file mode 100644 index 000000000000..60fead4a3374 --- /dev/null +++ b/linguistic/source/iprcache.cxx @@ -0,0 +1,263 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_linguistic.hxx" + +#include <string.h> + +#include "iprcache.hxx" +#include "linguistic/misc.hxx" + +#include <com/sun/star/linguistic2/DictionaryListEventFlags.hpp> +#include <tools/debug.hxx> +#include <osl/mutex.hxx> +#include <unotools/processfactory.hxx> +#include <linguistic/lngprops.hxx> + +using namespace utl; +using namespace osl; +using namespace com::sun::star; +using namespace com::sun::star::beans; +using namespace com::sun::star::lang; +using namespace com::sun::star::uno; +using namespace com::sun::star::linguistic2; + +using ::rtl::OUString; + +namespace linguistic +{ + + +#define NUM_FLUSH_PROPS 6 + +static const struct +{ + const char *pPropName; + sal_Int32 nPropHdl; +} aFlushProperties[ NUM_FLUSH_PROPS ] = +{ + { UPN_IS_USE_DICTIONARY_LIST, UPH_IS_USE_DICTIONARY_LIST }, + { UPN_IS_IGNORE_CONTROL_CHARACTERS, UPH_IS_IGNORE_CONTROL_CHARACTERS }, + { UPN_IS_SPELL_UPPER_CASE, UPH_IS_SPELL_UPPER_CASE }, + { UPN_IS_SPELL_WITH_DIGITS, UPH_IS_SPELL_WITH_DIGITS }, + { UPN_IS_SPELL_CAPITALIZATION, UPH_IS_SPELL_CAPITALIZATION } +}; + + +static void lcl_AddAsPropertyChangeListener( + Reference< XPropertyChangeListener > xListener, + Reference< XPropertySet > &rPropSet ) +{ + if (xListener.is() && rPropSet.is()) + { + for (int i = 0; i < NUM_FLUSH_PROPS; ++i) + { + rPropSet->addPropertyChangeListener( + ::rtl::OUString::createFromAscii(aFlushProperties[i].pPropName), xListener ); + } + } +} + + +static void lcl_RemoveAsPropertyChangeListener( + Reference< XPropertyChangeListener > xListener, + Reference< XPropertySet > &rPropSet ) +{ + if (xListener.is() && rPropSet.is()) + { + for (int i = 0; i < NUM_FLUSH_PROPS; ++i) + { + rPropSet->removePropertyChangeListener( + ::rtl::OUString::createFromAscii(aFlushProperties[i].pPropName), xListener ); + } + } +} + + +static sal_Bool lcl_IsFlushProperty( sal_Int32 nHandle ) +{ + int i; + for (i = 0; i < NUM_FLUSH_PROPS; ++i) + { + if (nHandle == aFlushProperties[i].nPropHdl) + break; + } + return i < NUM_FLUSH_PROPS; +} + + +FlushListener::FlushListener( Flushable *pFO ) +{ + SetFlushObj( pFO ); +} + + +FlushListener::~FlushListener() +{ +} + + +void FlushListener::SetDicList( Reference<XDictionaryList> &rDL ) +{ + MutexGuard aGuard( GetLinguMutex() ); + + if (xDicList != rDL) + { + if (xDicList.is()) + xDicList->removeDictionaryListEventListener( this ); + + xDicList = rDL; + if (xDicList.is()) + xDicList->addDictionaryListEventListener( this, sal_False ); + } +} + + +void FlushListener::SetPropSet( Reference< XPropertySet > &rPS ) +{ + MutexGuard aGuard( GetLinguMutex() ); + + if (xPropSet != rPS) + { + if (xPropSet.is()) + lcl_RemoveAsPropertyChangeListener( this, xPropSet ); + + xPropSet = rPS; + if (xPropSet.is()) + lcl_AddAsPropertyChangeListener( this, xPropSet ); + } +} + + +void SAL_CALL FlushListener::disposing( const EventObject& rSource ) + throw(RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + + if (xDicList.is() && rSource.Source == xDicList) + { + xDicList->removeDictionaryListEventListener( this ); + xDicList = NULL; //! release reference + } + if (xPropSet.is() && rSource.Source == xPropSet) + { + lcl_RemoveAsPropertyChangeListener( this, xPropSet ); + xPropSet = NULL; //! release reference + } +} + + +void SAL_CALL FlushListener::processDictionaryListEvent( + const DictionaryListEvent& rDicListEvent ) + throw(RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + + if (rDicListEvent.Source == xDicList) + { + sal_Int16 nEvt = rDicListEvent.nCondensedEvent; + sal_Int16 nFlushFlags = + DictionaryListEventFlags::ADD_NEG_ENTRY | + DictionaryListEventFlags::DEL_POS_ENTRY | + DictionaryListEventFlags::ACTIVATE_NEG_DIC | + DictionaryListEventFlags::DEACTIVATE_POS_DIC; + sal_Bool bFlush = 0 != (nEvt & nFlushFlags); + + DBG_ASSERT( pFlushObj, "missing object (NULL pointer)" ); + if (bFlush && pFlushObj != NULL) + pFlushObj->Flush(); + } +} + + +void SAL_CALL FlushListener::propertyChange( + const PropertyChangeEvent& rEvt ) + throw(RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + + if (rEvt.Source == xPropSet) + { + sal_Bool bFlush = lcl_IsFlushProperty( rEvt.PropertyHandle ); + + DBG_ASSERT( pFlushObj, "missing object (NULL pointer)" ); + if (bFlush && pFlushObj != NULL) + pFlushObj->Flush(); + } +} + + + +SpellCache::SpellCache() +{ + pFlushLstnr = new FlushListener( this ); + xFlushLstnr = pFlushLstnr; + Reference<XDictionaryList> aDictionaryList(GetDictionaryList()); + pFlushLstnr->SetDicList( aDictionaryList ); //! after reference is established + Reference<XPropertySet> aPropertySet(GetLinguProperties()); + pFlushLstnr->SetPropSet( aPropertySet ); //! after reference is established +} + +SpellCache::~SpellCache() +{ + Reference<XDictionaryList> aEmptyList; + Reference<XPropertySet> aEmptySet; + pFlushLstnr->SetDicList( aEmptyList ); + pFlushLstnr->SetPropSet( aEmptySet ); +} + +void SpellCache::Flush() +{ + MutexGuard aGuard( GetLinguMutex() ); + // clear word list + LangWordList_t aEmpty; + aWordLists.swap( aEmpty ); +} + +bool SpellCache::CheckWord( const OUString& rWord, LanguageType nLang ) +{ + MutexGuard aGuard( GetLinguMutex() ); + WordList_t &rList = aWordLists[ nLang ]; + const WordList_t::const_iterator aIt = rList.find( rWord ); + return aIt != rList.end(); +} + +void SpellCache::AddWord( const OUString& rWord, LanguageType nLang ) +{ + MutexGuard aGuard( GetLinguMutex() ); + WordList_t & rList = aWordLists[ nLang ]; + // occasional clean-up... + if (rList.size() > 500) + rList.clear(); + rList.insert( rWord ); +} + +} // namespace linguistic + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/linguistic/source/lng.component b/linguistic/source/lng.component new file mode 100755 index 000000000000..1b31003d7bda --- /dev/null +++ b/linguistic/source/lng.component @@ -0,0 +1,46 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!--********************************************************************** +* +* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +* +* Copyright 2000, 2010 Oracle and/or its affiliates. +* +* OpenOffice.org - a multi-platform office productivity suite +* +* This file is part of OpenOffice.org. +* +* OpenOffice.org is free software: you can redistribute it and/or modify +* it under the terms of the GNU Lesser General Public License version 3 +* only, as published by the Free Software Foundation. +* +* OpenOffice.org is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License version 3 for more details +* (a copy is included in the LICENSE file that accompanied this code). +* +* You should have received a copy of the GNU Lesser General Public License +* version 3 along with OpenOffice.org. If not, see +* <http://www.openoffice.org/license.html> +* for a copy of the LGPLv3 License. +* +**********************************************************************--> + +<component loader="com.sun.star.loader.SharedLibrary" prefix="lng" + xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="com.sun.star.lingu2.ConvDicList"> + <service name="com.sun.star.linguistic2.ConversionDictionaryList"/> + </implementation> + <implementation name="com.sun.star.lingu2.DicList"> + <service name="com.sun.star.linguistic2.DictionaryList"/> + </implementation> + <implementation name="com.sun.star.lingu2.LinguProps"> + <service name="com.sun.star.linguistic2.LinguProperties"/> + </implementation> + <implementation name="com.sun.star.lingu2.LngSvcMgr"> + <service name="com.sun.star.linguistic2.LinguServiceManager"/> + </implementation> + <implementation name="com.sun.star.lingu2.ProofreadingIterator"> + <service name="com.sun.star.linguistic2.ProofreadingIterator"/> + </implementation> +</component> diff --git a/linguistic/source/lngopt.cxx b/linguistic/source/lngopt.cxx new file mode 100644 index 000000000000..b7b2f1546015 --- /dev/null +++ b/linguistic/source/lngopt.cxx @@ -0,0 +1,677 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_linguistic.hxx" + +#include <sal/macros.h> +#include "lngopt.hxx" +#include "linguistic/lngprops.hxx" +#include "linguistic/misc.hxx" +#include <tools/debug.hxx> +#include <unotools/lingucfg.hxx> + +#include <uno/lbnames.h> // CPPU_CURRENT_LANGUAGE_BINDING_NAME macro, which specify the environment type +#include <cppuhelper/implbase1.hxx> // helper for implementations + +#include <cppuhelper/factory.hxx> // helper for factories +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/registry/XSimpleRegistry.hpp> +#include <com/sun/star/registry/XRegistryKey.hpp> +#include <com/sun/star/lang/Locale.hpp> +#include <com/sun/star/i18n/ScriptType.hpp> +#include <unotools/processfactory.hxx> +#include <i18npool/mslangid.hxx> + +using namespace utl; +using namespace osl; +using namespace com::sun::star; +using namespace com::sun::star::container; +using namespace com::sun::star::beans; +using namespace com::sun::star::lang; +using namespace com::sun::star::uno; +using namespace com::sun::star::linguistic2; +using namespace linguistic; + +using namespace com::sun::star::registry; + +using ::rtl::OUString; + + + +// static member intialization +SvtLinguOptions * LinguOptions::pData = NULL; +oslInterlockedCount LinguOptions::nRefCount; + + +LinguOptions::LinguOptions() +{ + if (!pData) + { + pData = new SvtLinguOptions; + SvtLinguConfig aLinguCfg; + aLinguCfg.GetOptions( *pData ); + } + + osl_incrementInterlockedCount( &nRefCount ); +} + + +LinguOptions::LinguOptions(const LinguOptions & /*rOpt*/) +{ + DBG_ASSERT( pData, "lng : data missing" ); + osl_incrementInterlockedCount( &nRefCount ); +} + + +LinguOptions::~LinguOptions() +{ + MutexGuard aGuard( GetLinguMutex() ); + + if ( osl_decrementInterlockedCount( &nRefCount ) == 0 ) + { + delete pData; pData = NULL; + } +} + + +sal_Bool LinguOptions::SetLocale_Impl( sal_Int16 &rLanguage, Any &rOld, const Any &rVal, sal_Int16 nType) +{ + sal_Bool bRes = sal_False; + + Locale aNew; + rVal >>= aNew; + sal_Int16 nNew = MsLangId::resolveSystemLanguageByScriptType(MsLangId::convertLocaleToLanguage(aNew), nType); + if (nNew != rLanguage) + { + Locale aLocale( CreateLocale( rLanguage ) ); + rOld.setValue( &aLocale, ::getCppuType((Locale*)0 )); + rLanguage = nNew; + bRes = sal_True; + } + + return bRes; +} + + +sal_Bool LinguOptions::SetValue( Any &rOld, const Any &rVal, sal_Int32 nWID ) +{ + MutexGuard aGuard( GetLinguMutex() ); + + sal_Bool bRes = sal_False; + + sal_Int16 *pnVal = 0; + sal_Bool *pbVal = 0; + + switch( nWID ) + { + case WID_IS_GERMAN_PRE_REFORM : /*! deprecated !*/ break; + case WID_IS_USE_DICTIONARY_LIST : pbVal = &pData->bIsUseDictionaryList; break; + case WID_IS_IGNORE_CONTROL_CHARACTERS : pbVal = &pData->bIsIgnoreControlCharacters; break; + case WID_IS_HYPH_AUTO : pbVal = &pData->bIsHyphAuto; break; + case WID_IS_HYPH_SPECIAL : pbVal = &pData->bIsHyphSpecial; break; + case WID_IS_SPELL_AUTO : pbVal = &pData->bIsSpellAuto; break; + case WID_IS_SPELL_HIDE : /*! deprecated !*/ break; + case WID_IS_SPELL_IN_ALL_LANGUAGES :/*! deprecated !*/ break; + case WID_IS_SPELL_SPECIAL : pbVal = &pData->bIsSpellSpecial; break; + case WID_IS_WRAP_REVERSE : pbVal = &pData->bIsSpellReverse; break; + case WID_DEFAULT_LANGUAGE : pnVal = &pData->nDefaultLanguage; break; + case WID_IS_SPELL_CAPITALIZATION : pbVal = &pData->bIsSpellCapitalization; break; + case WID_IS_SPELL_WITH_DIGITS : pbVal = &pData->bIsSpellWithDigits; break; + case WID_IS_SPELL_UPPER_CASE : pbVal = &pData->bIsSpellUpperCase; break; + case WID_HYPH_MIN_LEADING : pnVal = &pData->nHyphMinLeading; break; + case WID_HYPH_MIN_TRAILING : pnVal = &pData->nHyphMinTrailing; break; + case WID_HYPH_MIN_WORD_LENGTH : pnVal = &pData->nHyphMinWordLength; break; + case WID_DEFAULT_LOCALE : + { + bRes = SetLocale_Impl( pData->nDefaultLanguage, rOld, rVal, ::com::sun::star::i18n::ScriptType::LATIN ); + break; + } + case WID_DEFAULT_LOCALE_CJK : + { + bRes = SetLocale_Impl( pData->nDefaultLanguage_CJK, rOld, rVal, ::com::sun::star::i18n::ScriptType::ASIAN ); + break; + } + case WID_DEFAULT_LOCALE_CTL : + { + bRes = SetLocale_Impl( pData->nDefaultLanguage_CTL, rOld, rVal, ::com::sun::star::i18n::ScriptType::COMPLEX ); + break; + } + default : + { + DBG_ASSERT( 0,"lng : unknown WID"); + bRes = sal_False; + } + } + + if (pbVal) + { + sal_Bool bNew = sal_False; + rVal >>= bNew; + if (bNew != *pbVal) + { + rOld <<= *pbVal; + *pbVal = bNew; + bRes = sal_True; + } + } + if (pnVal) + { + sal_Int16 nNew = 0; + rVal >>= nNew; + if (nNew != *pnVal) + { + rOld <<= *pnVal; + *pnVal = nNew; + bRes = sal_True; + } + } + + + return bRes; +} + +void LinguOptions::GetValue( Any &rVal, sal_Int32 nWID ) const +{ + MutexGuard aGuard( GetLinguMutex() ); + + sal_Int16 *pnVal = 0; + sal_Bool *pbVal = 0; + sal_Bool bDummy = sal_False; + + switch( nWID ) + { + case WID_IS_GERMAN_PRE_REFORM : pbVal = &bDummy; /*! deprecated !*/ break; + case WID_IS_USE_DICTIONARY_LIST : pbVal = &pData->bIsUseDictionaryList; break; + case WID_IS_IGNORE_CONTROL_CHARACTERS : pbVal = &pData->bIsIgnoreControlCharacters; break; + case WID_IS_HYPH_AUTO : pbVal = &pData->bIsHyphAuto; break; + case WID_IS_HYPH_SPECIAL : pbVal = &pData->bIsHyphSpecial; break; + case WID_IS_SPELL_AUTO : pbVal = &pData->bIsSpellAuto; break; + case WID_IS_SPELL_HIDE : pbVal = &bDummy; /*! deprecated !*/ break; + case WID_IS_SPELL_IN_ALL_LANGUAGES :pbVal = &bDummy; /*! deprecated !*/ break; + case WID_IS_SPELL_SPECIAL : pbVal = &pData->bIsSpellSpecial; break; + case WID_IS_WRAP_REVERSE : pbVal = &pData->bIsSpellReverse; break; + case WID_DEFAULT_LANGUAGE : pnVal = &pData->nDefaultLanguage; break; + case WID_IS_SPELL_CAPITALIZATION : pbVal = &pData->bIsSpellCapitalization; break; + case WID_IS_SPELL_WITH_DIGITS : pbVal = &pData->bIsSpellWithDigits; break; + case WID_IS_SPELL_UPPER_CASE : pbVal = &pData->bIsSpellUpperCase; break; + case WID_HYPH_MIN_LEADING : pnVal = &pData->nHyphMinLeading; break; + case WID_HYPH_MIN_TRAILING : pnVal = &pData->nHyphMinTrailing; break; + case WID_HYPH_MIN_WORD_LENGTH : pnVal = &pData->nHyphMinWordLength; break; + case WID_DEFAULT_LOCALE : + { + Locale aLocale( MsLangId::convertLanguageToLocale( pData->nDefaultLanguage ) ); + rVal.setValue( &aLocale, ::getCppuType((Locale*)0 )); + break; + } + case WID_DEFAULT_LOCALE_CJK : + { + Locale aLocale( MsLangId::convertLanguageToLocale( pData->nDefaultLanguage_CJK ) ); + rVal.setValue( &aLocale, ::getCppuType((Locale*)0 )); + break; + } + case WID_DEFAULT_LOCALE_CTL : + { + Locale aLocale( MsLangId::convertLanguageToLocale( pData->nDefaultLanguage_CTL ) ); + rVal.setValue( &aLocale, ::getCppuType((Locale*)0 )); + break; + } + default : + { + DBG_ASSERT( 0,"lng : unknown WID"); + } + } + + if (pbVal) + rVal <<= *pbVal; + if (pnVal) + rVal <<= *pnVal; +} + + +struct WID_Name +{ + sal_Int32 nWID; + const char *pPropertyName; +}; + +//! order of entries is import (see LinguOptions::GetName) +//! since the WID is used as index in this table! +WID_Name aWID_Name[] = +{ + { 0, 0 }, + { WID_IS_USE_DICTIONARY_LIST, UPN_IS_USE_DICTIONARY_LIST }, + { WID_IS_IGNORE_CONTROL_CHARACTERS, UPN_IS_IGNORE_CONTROL_CHARACTERS }, + { WID_IS_SPELL_UPPER_CASE, UPN_IS_SPELL_UPPER_CASE }, + { WID_IS_SPELL_WITH_DIGITS, UPN_IS_SPELL_WITH_DIGITS }, + { WID_IS_SPELL_CAPITALIZATION, UPN_IS_SPELL_CAPITALIZATION }, + { WID_HYPH_MIN_LEADING, UPN_HYPH_MIN_LEADING }, + { WID_HYPH_MIN_TRAILING, UPN_HYPH_MIN_TRAILING }, + { WID_HYPH_MIN_WORD_LENGTH, UPN_HYPH_MIN_WORD_LENGTH }, + { WID_DEFAULT_LOCALE, UPN_DEFAULT_LOCALE }, + { WID_IS_SPELL_AUTO, UPN_IS_SPELL_AUTO }, + { 0, 0 }, + { 0, 0 }, + { WID_IS_SPELL_SPECIAL, UPN_IS_SPELL_SPECIAL }, + { WID_IS_HYPH_AUTO, UPN_IS_HYPH_AUTO }, + { WID_IS_HYPH_SPECIAL, UPN_IS_HYPH_SPECIAL }, + { WID_IS_WRAP_REVERSE, UPN_IS_WRAP_REVERSE }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { WID_DEFAULT_LANGUAGE, UPN_DEFAULT_LANGUAGE }, + { WID_DEFAULT_LOCALE_CJK, UPN_DEFAULT_LOCALE_CJK }, + { WID_DEFAULT_LOCALE_CTL, UPN_DEFAULT_LOCALE_CTL } +}; + + +OUString LinguOptions::GetName( sal_Int32 nWID ) +{ + MutexGuard aGuard( GetLinguMutex() ); + + OUString aRes; + + sal_Int32 nLen = SAL_N_ELEMENTS( aWID_Name ); + if (0 <= nWID && nWID < nLen && aWID_Name[ nWID ].nWID == nWID) + aRes = OUString::createFromAscii(aWID_Name[nWID].pPropertyName); + else + OSL_FAIL("lng : unknown WID"); + + return aRes; +} + + + +//! map must be sorted by first entry in alphabetical increasing order. +const SfxItemPropertyMapEntry* lcl_GetLinguProps() +{ + static const SfxItemPropertyMapEntry aLinguProps[] = + { + { MAP_CHAR_LEN(UPN_DEFAULT_LANGUAGE), WID_DEFAULT_LANGUAGE, + &::getCppuType( (sal_Int16*)0 ), 0, 0 }, + { MAP_CHAR_LEN(UPN_DEFAULT_LOCALE), WID_DEFAULT_LOCALE, + &::getCppuType( (Locale* )0), 0, 0 }, + { MAP_CHAR_LEN(UPN_DEFAULT_LOCALE_CJK), WID_DEFAULT_LOCALE_CJK, + &::getCppuType( (Locale* )0), 0, 0 }, + { MAP_CHAR_LEN(UPN_DEFAULT_LOCALE_CTL), WID_DEFAULT_LOCALE_CTL, + &::getCppuType( (Locale* )0), 0, 0 }, + { MAP_CHAR_LEN(UPN_HYPH_MIN_LEADING), WID_HYPH_MIN_LEADING, + &::getCppuType( (sal_Int16*)0 ), 0, 0 }, + { MAP_CHAR_LEN(UPN_HYPH_MIN_TRAILING), WID_HYPH_MIN_TRAILING, + &::getCppuType( (sal_Int16*)0 ), 0, 0 }, + { MAP_CHAR_LEN(UPN_HYPH_MIN_WORD_LENGTH), WID_HYPH_MIN_WORD_LENGTH, + &::getCppuType( (sal_Int16*)0 ), 0, 0 }, + { MAP_CHAR_LEN(UPN_IS_GERMAN_PRE_REFORM), WID_IS_GERMAN_PRE_REFORM, /*! deprecated !*/ + &::getBooleanCppuType(), 0, 0 }, + { MAP_CHAR_LEN(UPN_IS_HYPH_AUTO), WID_IS_HYPH_AUTO, + &::getBooleanCppuType(), 0, 0 }, + { MAP_CHAR_LEN(UPN_IS_HYPH_SPECIAL), WID_IS_HYPH_SPECIAL, + &::getBooleanCppuType(), 0, 0 }, + { MAP_CHAR_LEN(UPN_IS_IGNORE_CONTROL_CHARACTERS), WID_IS_IGNORE_CONTROL_CHARACTERS, + &::getBooleanCppuType(), 0, 0 }, + { MAP_CHAR_LEN(UPN_IS_SPELL_AUTO), WID_IS_SPELL_AUTO, + &::getBooleanCppuType(), 0, 0 }, + { MAP_CHAR_LEN(UPN_IS_SPELL_CAPITALIZATION), WID_IS_SPELL_CAPITALIZATION, + &::getBooleanCppuType(), 0, 0 }, + { MAP_CHAR_LEN(UPN_IS_SPELL_HIDE), WID_IS_SPELL_HIDE, /*! deprecated !*/ + &::getBooleanCppuType(), 0, 0 }, + { MAP_CHAR_LEN(UPN_IS_SPELL_IN_ALL_LANGUAGES), WID_IS_SPELL_IN_ALL_LANGUAGES, /*! deprecated !*/ + &::getBooleanCppuType(), 0, 0 }, + { MAP_CHAR_LEN(UPN_IS_SPELL_SPECIAL), WID_IS_SPELL_SPECIAL, + &::getBooleanCppuType(), 0, 0 }, + { MAP_CHAR_LEN(UPN_IS_SPELL_UPPER_CASE), WID_IS_SPELL_UPPER_CASE, + &::getBooleanCppuType(), 0, 0 }, + { MAP_CHAR_LEN(UPN_IS_SPELL_WITH_DIGITS), WID_IS_SPELL_WITH_DIGITS, + &::getBooleanCppuType(), 0, 0 }, + { MAP_CHAR_LEN(UPN_IS_USE_DICTIONARY_LIST), WID_IS_USE_DICTIONARY_LIST, + &::getBooleanCppuType(), 0, 0 }, + { MAP_CHAR_LEN(UPN_IS_WRAP_REVERSE), WID_IS_WRAP_REVERSE, + &::getBooleanCppuType(), 0, 0 }, + { 0,0,0,0,0,0 } + }; + return aLinguProps; +} +LinguProps::LinguProps() : + aEvtListeners (GetLinguMutex()), + aPropListeners (GetLinguMutex()), + aPropertyMap(lcl_GetLinguProps()) +{ + bDisposing = sal_False; +} + +void LinguProps::launchEvent( const PropertyChangeEvent &rEvt ) const +{ + cppu::OInterfaceContainerHelper *pContainer = + aPropListeners.getContainer( rEvt.PropertyHandle ); + if (pContainer) + { + cppu::OInterfaceIteratorHelper aIt( *pContainer ); + while (aIt.hasMoreElements()) + { + Reference< XPropertyChangeListener > xRef( aIt.next(), UNO_QUERY ); + if (xRef.is()) + xRef->propertyChange( rEvt ); + } + } +} + +Reference< XInterface > SAL_CALL LinguProps_CreateInstance( + const Reference< XMultiServiceFactory > & /*rSMgr*/ ) + throw(Exception) +{ + Reference< XInterface > xService = (cppu::OWeakObject*)new LinguProps; + return xService; +} + +Reference< XPropertySetInfo > SAL_CALL LinguProps::getPropertySetInfo() + throw(RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + + static Reference< XPropertySetInfo > aRef = + new SfxItemPropertySetInfo( &aPropertyMap ); + return aRef; +} + +void SAL_CALL LinguProps::setPropertyValue( + const OUString& rPropertyName, const Any& rValue ) + throw(UnknownPropertyException, PropertyVetoException, + IllegalArgumentException, WrappedTargetException, RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + + const SfxItemPropertySimpleEntry* pCur = aPropertyMap.getByName( rPropertyName ); + if (pCur) + { + Any aOld( aConfig.GetProperty( pCur->nWID ) ); + if (aOld != rValue && aConfig.SetProperty( pCur->nWID, rValue )) + { + PropertyChangeEvent aChgEvt( (XPropertySet *) this, rPropertyName, + sal_False, pCur->nWID, aOld, rValue ); + launchEvent( aChgEvt ); + } + } +#ifdef LINGU_EXCEPTIONS + else + { + throw UnknownPropertyException(); + } +#endif +} + +Any SAL_CALL LinguProps::getPropertyValue( const OUString& rPropertyName ) + throw(UnknownPropertyException, WrappedTargetException, RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + + Any aRet; + + const SfxItemPropertySimpleEntry* pCur = aPropertyMap.getByName( rPropertyName ); + if(pCur) + { + aRet = aConfig.GetProperty( pCur->nWID ); + } +#ifdef LINGU_EXCEPTIONS + else + { + throw UnknownPropertyException(); + } +#endif + + return aRet; +} + +void SAL_CALL LinguProps::addPropertyChangeListener( + const OUString& rPropertyName, + const Reference< XPropertyChangeListener >& rxListener ) + throw(UnknownPropertyException, WrappedTargetException, RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + + if (!bDisposing && rxListener.is()) + { + const SfxItemPropertySimpleEntry* pCur = aPropertyMap.getByName( rPropertyName ); + if(pCur) + aPropListeners.addInterface( pCur->nWID, rxListener ); +#ifdef LINGU_EXCEPTIONS + else + { + throw UnknownPropertyException(); + } +#endif + } +} + +void SAL_CALL LinguProps::removePropertyChangeListener( + const OUString& rPropertyName, + const Reference< XPropertyChangeListener >& rxListener ) + throw(UnknownPropertyException, WrappedTargetException, RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + + if (!bDisposing && rxListener.is()) + { + const SfxItemPropertySimpleEntry* pCur = aPropertyMap.getByName( rPropertyName ); + if(pCur) + aPropListeners.removeInterface( pCur->nWID, rxListener ); +#ifdef LINGU_EXCEPTIONS + else + { + throw UnknownPropertyException(); + } +#endif + } +} + +void SAL_CALL LinguProps::addVetoableChangeListener( + const OUString& /*rPropertyName*/, + const Reference< XVetoableChangeListener >& /*xListener*/ ) + throw(UnknownPropertyException, WrappedTargetException, RuntimeException) +{ +} + +void SAL_CALL LinguProps::removeVetoableChangeListener( + const OUString& /*rPropertyName*/, + const Reference< XVetoableChangeListener >& /*xListener*/ ) + throw(UnknownPropertyException, WrappedTargetException, RuntimeException) +{ +} + + +void SAL_CALL LinguProps::setFastPropertyValue( sal_Int32 nHandle, const Any& rValue ) + throw(UnknownPropertyException, PropertyVetoException, + IllegalArgumentException, WrappedTargetException, RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + + Any aOld( aConfig.GetProperty( nHandle ) ); + if (aOld != rValue && aConfig.SetProperty( nHandle, rValue )) + { + PropertyChangeEvent aChgEvt( (XPropertySet *) this, + LinguOptions::GetName( nHandle ), sal_False, nHandle, aOld, rValue ); + launchEvent( aChgEvt ); + } +} + + +Any SAL_CALL LinguProps::getFastPropertyValue( sal_Int32 nHandle ) + throw(UnknownPropertyException, WrappedTargetException, RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + + Any aRes( aConfig.GetProperty( nHandle ) ); + return aRes; +} + + +Sequence< PropertyValue > SAL_CALL + LinguProps::getPropertyValues() + throw(RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + + sal_Int32 nLen = aPropertyMap.getSize(); + Sequence< PropertyValue > aProps( nLen ); + PropertyValue *pProp = aProps.getArray(); + PropertyEntryVector_t aPropEntries = aPropertyMap.getPropertyEntries(); + PropertyEntryVector_t::const_iterator aIt = aPropEntries.begin(); + for (sal_Int32 i = 0; i < nLen; ++i, ++aIt) + { + PropertyValue &rVal = pProp[i]; + Any aAny( aConfig.GetProperty( aIt->nWID ) ); + + rVal.Name = aIt->sName; + rVal.Handle = aIt->nWID; + rVal.Value = aAny; + rVal.State = PropertyState_DIRECT_VALUE ; + } + return aProps; +} + +void SAL_CALL + LinguProps::setPropertyValues( const Sequence< PropertyValue >& rProps ) + throw(UnknownPropertyException, PropertyVetoException, + IllegalArgumentException, WrappedTargetException, RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + + sal_Int32 nLen = rProps.getLength(); + const PropertyValue *pVal = rProps.getConstArray(); + for (sal_Int32 i = 0; i < nLen; ++i) + { + const PropertyValue &rVal = pVal[i]; + setPropertyValue( rVal.Name, rVal.Value ); + } +} + +void SAL_CALL + LinguProps::dispose() + throw(RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + + if (!bDisposing) + { + bDisposing = sal_True; + + //! its too late to save the options here! + // (see AppExitListener for saving) + //aOpt.Save(); // save (possible) changes before exiting + + EventObject aEvtObj( (XPropertySet *) this ); + aEvtListeners.disposeAndClear( aEvtObj ); + aPropListeners.disposeAndClear( aEvtObj ); + } +} + +void SAL_CALL + LinguProps::addEventListener( const Reference< XEventListener >& rxListener ) + throw(RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + + if (!bDisposing && rxListener.is()) + aEvtListeners.addInterface( rxListener ); +} + +void SAL_CALL + LinguProps::removeEventListener( const Reference< XEventListener >& rxListener ) + throw(RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + + if (!bDisposing && rxListener.is()) + aEvtListeners.removeInterface( rxListener ); +} + + +// Service specific part + +// XServiceInfo +OUString SAL_CALL LinguProps::getImplementationName() + throw(RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + return getImplementationName_Static(); +} + +// XServiceInfo +sal_Bool SAL_CALL LinguProps::supportsService( const OUString& ServiceName ) + throw(RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + + uno::Sequence< OUString > aSNL = getSupportedServiceNames(); + const OUString * pArray = aSNL.getConstArray(); + for( sal_Int32 i = 0; i < aSNL.getLength(); i++ ) + if( pArray[i] == ServiceName ) + return sal_True; + return sal_False; +} + +// XServiceInfo +uno::Sequence< OUString > SAL_CALL LinguProps::getSupportedServiceNames() + throw(RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + return getSupportedServiceNames_Static(); +} + +// ORegistryServiceManager_Static +uno::Sequence< OUString > LinguProps::getSupportedServiceNames_Static() + throw() +{ + MutexGuard aGuard( GetLinguMutex() ); + + uno::Sequence< OUString > aSNS( 1 ); // more than 1 service possible + aSNS.getArray()[0] = A2OU( SN_LINGU_PROPERTIES ); + return aSNS; +} + +void * SAL_CALL LinguProps_getFactory( const sal_Char * pImplName, + XMultiServiceFactory *pServiceManager, void * ) +{ + void * pRet = 0; + if ( !LinguProps::getImplementationName_Static().compareToAscii( pImplName ) ) + { + Reference< XSingleServiceFactory > xFactory = + cppu::createOneInstanceFactory( + pServiceManager, + LinguProps::getImplementationName_Static(), + LinguProps_CreateInstance, + LinguProps::getSupportedServiceNames_Static()); + // acquire, because we return an interface pointer instead of a reference + xFactory->acquire(); + pRet = xFactory.get(); + } + return pRet; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/linguistic/source/lngopt.hxx b/linguistic/source/lngopt.hxx new file mode 100644 index 000000000000..d0e8ae556496 --- /dev/null +++ b/linguistic/source/lngopt.hxx @@ -0,0 +1,182 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _LINGUISTIC_LNGOPT_HHX_ +#define _LINGUISTIC_LNGOPT_HHX_ + +#include <functional> + +#include <uno/lbnames.h> // CPPU_CURRENT_LANGUAGE_BINDING_NAME macro, which specify the environment type +#include <cppuhelper/implbase5.hxx> // helper for implementations +#include <cppuhelper/interfacecontainer.hxx> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/beans/XFastPropertySet.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/beans/XPropertyAccess.hpp> +#include <com/sun/star/lang/XComponent.hpp> +#include <unotools/lingucfg.hxx> +#include <svl/itemprop.hxx> +#include <unotools/configitem.hxx> +#include <com/sun/star/uno/Any.h> +#include <tools/solar.h> + +#include <svl/itemprop.hxx> +#include "linguistic/misc.hxx" +#include "defs.hxx" + +namespace com { namespace sun { namespace star { + namespace beans { + struct PropertyChangeEvent; + } + namespace registry { + class XRegistryKey; + } +}}} + + + +// LinguOptions +// This class represents all Linguistik relevant options. + +class LinguOptions +{ + static SvtLinguOptions *pData; + static oslInterlockedCount nRefCount; // number of objects of this class + + //! uses default assignment-operator + + sal_Bool SetLocale_Impl( sal_Int16 &rLanguage, + ::com::sun::star::uno::Any &rOld, + const ::com::sun::star::uno::Any &rVal, sal_Int16 nType ); + +public: + LinguOptions(); + LinguOptions(const LinguOptions &rOpt); + ~LinguOptions(); + + sal_Bool SetValue( ::com::sun::star::uno::Any &rOld, + const ::com::sun::star::uno::Any &rVal, sal_Int32 nWID ); + void GetValue( ::com::sun::star::uno::Any &rVal, sal_Int32 nWID ) const; + + static ::rtl::OUString GetName( sal_Int32 nWID ); + + const ::com::sun::star::uno::Sequence< rtl::OUString > + GetActiveDics() const { return pData->aActiveDics; } + + const ::com::sun::star::uno::Sequence< rtl::OUString > + GetActiveConvDics() const { return pData->aActiveConvDics; } +}; + + + +// uses templates from <cppuhelper/interfacecontainer.h> + + +// helper function call class +struct PropHashType_Impl +{ + size_t operator()(const sal_Int32 &s) const { return s; } +}; + +typedef cppu::OMultiTypeInterfaceContainerHelperVar + < + sal_Int32, + PropHashType_Impl, + std::equal_to< sal_Int32 > + > OPropertyListenerContainerHelper; + + + +class LinguProps : + public cppu::WeakImplHelper5 + < + com::sun::star::beans::XPropertySet, + com::sun::star::beans::XFastPropertySet, + com::sun::star::beans::XPropertyAccess, + com::sun::star::lang::XComponent, + com::sun::star::lang::XServiceInfo + > +{ + ::cppu::OInterfaceContainerHelper aEvtListeners; + OPropertyListenerContainerHelper aPropListeners; + + SfxItemPropertyMap aPropertyMap; + SvtLinguConfig aConfig; + + sal_Bool bDisposing; + + // disallow copy-constructor and assignment-operator for now + LinguProps(const LinguProps &); + LinguProps & operator = (const LinguProps &); + + void launchEvent( const ::com::sun::star::beans::PropertyChangeEvent &rEvt ) const; + +public: + LinguProps(); + + // XPropertySet + virtual ::com::sun::star::uno::Reference< ::com::sun::star::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo() throw(::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL setPropertyValue( const ::rtl::OUString& aPropertyName, const ::com::sun::star::uno::Any& aValue ) throw(::com::sun::star::beans::UnknownPropertyException, ::com::sun::star::beans::PropertyVetoException, ::com::sun::star::lang::IllegalArgumentException, ::com::sun::star::lang::WrappedTargetException, ::com::sun::star::uno::RuntimeException); + virtual ::com::sun::star::uno::Any SAL_CALL getPropertyValue( const ::rtl::OUString& PropertyName ) throw(::com::sun::star::beans::UnknownPropertyException, ::com::sun::star::lang::WrappedTargetException, ::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL addPropertyChangeListener( const ::rtl::OUString& aPropertyName, const ::com::sun::star::uno::Reference< ::com::sun::star::beans::XPropertyChangeListener >& rxListener ) throw(::com::sun::star::beans::UnknownPropertyException, ::com::sun::star::lang::WrappedTargetException, ::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL removePropertyChangeListener( const ::rtl::OUString& aPropertyName, const ::com::sun::star::uno::Reference< ::com::sun::star::beans::XPropertyChangeListener >& rxListener ) throw(::com::sun::star::beans::UnknownPropertyException, ::com::sun::star::lang::WrappedTargetException, ::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL addVetoableChangeListener( const ::rtl::OUString& PropertyName, const ::com::sun::star::uno::Reference< ::com::sun::star::beans::XVetoableChangeListener >& rxListener ) throw(::com::sun::star::beans::UnknownPropertyException, ::com::sun::star::lang::WrappedTargetException, ::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL removeVetoableChangeListener( const ::rtl::OUString& PropertyName, const ::com::sun::star::uno::Reference< ::com::sun::star::beans::XVetoableChangeListener >& rxListener ) throw(::com::sun::star::beans::UnknownPropertyException, ::com::sun::star::lang::WrappedTargetException, ::com::sun::star::uno::RuntimeException); + + // XFastPropertySet + virtual void SAL_CALL setFastPropertyValue( sal_Int32 nHandle, const ::com::sun::star::uno::Any& aValue ) throw(::com::sun::star::beans::UnknownPropertyException, ::com::sun::star::beans::PropertyVetoException, ::com::sun::star::lang::IllegalArgumentException, ::com::sun::star::lang::WrappedTargetException, ::com::sun::star::uno::RuntimeException); + virtual ::com::sun::star::uno::Any SAL_CALL getFastPropertyValue( sal_Int32 nHandle ) throw(::com::sun::star::beans::UnknownPropertyException, ::com::sun::star::lang::WrappedTargetException, ::com::sun::star::uno::RuntimeException); + + // XPropertyAccess + virtual ::com::sun::star::uno::Sequence< ::com::sun::star::beans::PropertyValue > SAL_CALL getPropertyValues() throw(::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL setPropertyValues( const ::com::sun::star::uno::Sequence< ::com::sun::star::beans::PropertyValue >& aProps ) throw(::com::sun::star::beans::UnknownPropertyException, ::com::sun::star::beans::PropertyVetoException, ::com::sun::star::lang::IllegalArgumentException, ::com::sun::star::lang::WrappedTargetException, ::com::sun::star::uno::RuntimeException); + + // XComponent + virtual void SAL_CALL dispose() throw(::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL addEventListener( const ::com::sun::star::uno::Reference< ::com::sun::star::lang::XEventListener >& rxListener ) throw(::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL removeEventListener( const ::com::sun::star::uno::Reference< ::com::sun::star::lang::XEventListener >& rxListener ) throw(::com::sun::star::uno::RuntimeException); + + // XServiceInfo + virtual ::rtl::OUString SAL_CALL getImplementationName() throw(::com::sun::star::uno::RuntimeException); + virtual sal_Bool SAL_CALL supportsService( const ::rtl::OUString& ServiceName ) throw(::com::sun::star::uno::RuntimeException); + virtual ::com::sun::star::uno::Sequence< ::rtl::OUString > SAL_CALL getSupportedServiceNames() throw(::com::sun::star::uno::RuntimeException); + + + static inline ::rtl::OUString getImplementationName_Static() throw(); + static com::sun::star::uno::Sequence< ::rtl::OUString > getSupportedServiceNames_Static() throw(); +}; + +inline ::rtl::OUString LinguProps::getImplementationName_Static() throw() +{ + return A2OU( "com.sun.star.lingu2.LinguProps" ); +} + + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/linguistic/source/lngprophelp.cxx b/linguistic/source/lngprophelp.cxx new file mode 100644 index 000000000000..41565a3f1886 --- /dev/null +++ b/linguistic/source/lngprophelp.cxx @@ -0,0 +1,861 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_linguistic.hxx" + +#include <tools/debug.hxx> +#include <sal/macros.h> + +#include <com/sun/star/linguistic2/LinguServiceEvent.hpp> +#include <com/sun/star/linguistic2/LinguServiceEventFlags.hpp> +#include <com/sun/star/linguistic2/XLinguServiceEventListener.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <osl/mutex.hxx> + +#include <linguistic/misc.hxx> +#include <linguistic/lngprops.hxx> + +#include <linguistic/lngprophelp.hxx> + +using namespace osl; +using namespace com::sun::star; +using namespace com::sun::star::beans; +using namespace com::sun::star::lang; +using namespace com::sun::star::uno; +using namespace com::sun::star::linguistic2; +using namespace linguistic; + +using ::rtl::OUString; + +namespace linguistic +{ + + +static const char *aCH[] = +{ + UPN_IS_IGNORE_CONTROL_CHARACTERS, + UPN_IS_USE_DICTIONARY_LIST, +}; + +static int nCHCount = SAL_N_ELEMENTS(aCH); + + +PropertyChgHelper::PropertyChgHelper( + const Reference< XInterface > &rxSource, + Reference< XPropertySet > &rxPropSet, + int nAllowedEvents ) : + PropertyChgHelperBase(), + aPropNames (nCHCount), + xMyEvtObj (rxSource), + aLngSvcEvtListeners (GetLinguMutex()), + xPropSet (rxPropSet), + nEvtFlags (nAllowedEvents) +{ + OUString *pName = aPropNames.getArray(); + for (sal_Int32 i = 0; i < nCHCount; ++i) + { + pName[i] = ::rtl::OUString::createFromAscii( aCH[i] ); + } + + SetDefaultValues(); +} + + +PropertyChgHelper::PropertyChgHelper( const PropertyChgHelper &rHelper ) : + PropertyChgHelperBase(), + aLngSvcEvtListeners (GetLinguMutex()) +{ + RemoveAsPropListener(); + aPropNames = rHelper.aPropNames; + xMyEvtObj = rHelper.xMyEvtObj; + xPropSet = rHelper.xPropSet; + nEvtFlags = rHelper.nEvtFlags; + AddAsPropListener(); + + SetDefaultValues(); + GetCurrentValues(); +} + + +PropertyChgHelper::~PropertyChgHelper() +{ +} + + +void PropertyChgHelper::AddPropNames( const char *pNewNames[], sal_Int32 nCount ) +{ + if (pNewNames && nCount) + { + sal_Int32 nLen = GetPropNames().getLength(); + GetPropNames().realloc( nLen + nCount ); + OUString *pName = GetPropNames().getArray(); + for (sal_Int32 i = 0; i < nCount; ++i) + { + pName[ nLen + i ] = ::rtl::OUString::createFromAscii( pNewNames[ i ] ); + + } + } +} + + +void PropertyChgHelper::SetDefaultValues() +{ + bResIsIgnoreControlCharacters = bIsIgnoreControlCharacters = sal_True; + bResIsUseDictionaryList = bIsUseDictionaryList = sal_True; +} + + +void PropertyChgHelper::GetCurrentValues() +{ + sal_Int32 nLen = GetPropNames().getLength(); + if (GetPropSet().is() && nLen) + { + const OUString *pPropName = GetPropNames().getConstArray(); + for (sal_Int32 i = 0; i < nLen; ++i) + { + sal_Bool *pbVal = NULL, + *pbResVal = NULL; + + if (pPropName[i].equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( UPN_IS_IGNORE_CONTROL_CHARACTERS ) )) + { + pbVal = &bIsIgnoreControlCharacters; + pbResVal = &bResIsIgnoreControlCharacters; + } + else if (pPropName[i].equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( UPN_IS_USE_DICTIONARY_LIST ) )) + { + pbVal = &bIsUseDictionaryList; + pbResVal = &bResIsUseDictionaryList; + } + + if (pbVal && pbResVal) + { + GetPropSet()->getPropertyValue( pPropName[i] ) >>= *pbVal; + *pbResVal = *pbVal; + } + } + } +} + + +void PropertyChgHelper::SetTmpPropVals( const PropertyValues &rPropVals ) +{ + // return value is default value unless there is an explicitly supplied + // temporary value + bResIsIgnoreControlCharacters = bIsIgnoreControlCharacters; + bResIsUseDictionaryList = bIsUseDictionaryList; + + sal_Int32 nLen = rPropVals.getLength(); + if (nLen) + { + const PropertyValue *pVal = rPropVals.getConstArray(); + for (sal_Int32 i = 0; i < nLen; ++i) + { + sal_Bool *pbResVal = NULL; + switch (pVal[i].Handle) + { + case UPH_IS_IGNORE_CONTROL_CHARACTERS : + pbResVal = &bResIsIgnoreControlCharacters; break; + case UPH_IS_USE_DICTIONARY_LIST : + pbResVal = &bResIsUseDictionaryList; break; + default: + ; + } + if (pbResVal) + pVal[i].Value >>= *pbResVal; + } + } +} + + +sal_Bool PropertyChgHelper::propertyChange_Impl( const PropertyChangeEvent& rEvt ) +{ + sal_Bool bRes = sal_False; + + if (GetPropSet().is() && rEvt.Source == GetPropSet()) + { + sal_Int16 nLngSvcFlags = (nEvtFlags & AE_HYPHENATOR) ? + LinguServiceEventFlags::HYPHENATE_AGAIN : 0; + sal_Bool bSCWA = sal_False, // SPELL_CORRECT_WORDS_AGAIN ? + bSWWA = sal_False; // SPELL_WRONG_WORDS_AGAIN ? + + sal_Bool *pbVal = NULL; + switch (rEvt.PropertyHandle) + { + case UPH_IS_IGNORE_CONTROL_CHARACTERS : + { + pbVal = &bIsIgnoreControlCharacters; + nLngSvcFlags = 0; + break; + } + case UPH_IS_USE_DICTIONARY_LIST : + { + pbVal = &bIsUseDictionaryList; + bSCWA = bSWWA = sal_True; + break; + } + default: + { + bRes = sal_False; + } + } + if (pbVal) + rEvt.NewValue >>= *pbVal; + + bRes = 0 != pbVal; // sth changed? + if (bRes) + { + sal_Bool bSpellEvts = (nEvtFlags & AE_SPELLCHECKER) ? sal_True : sal_False; + if (bSCWA && bSpellEvts) + nLngSvcFlags |= LinguServiceEventFlags::SPELL_CORRECT_WORDS_AGAIN; + if (bSWWA && bSpellEvts) + nLngSvcFlags |= LinguServiceEventFlags::SPELL_WRONG_WORDS_AGAIN; + if (nLngSvcFlags) + { + LinguServiceEvent aEvt( GetEvtObj(), nLngSvcFlags ); + LaunchEvent( aEvt ); + } + } + } + + return bRes; +} + + +void SAL_CALL + PropertyChgHelper::propertyChange( const PropertyChangeEvent& rEvt ) + throw(RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + propertyChange_Impl( rEvt ); +} + + +void PropertyChgHelper::AddAsPropListener() +{ + if (xPropSet.is()) + { + sal_Int32 nLen = aPropNames.getLength(); + const OUString *pPropName = aPropNames.getConstArray(); + for (sal_Int32 i = 0; i < nLen; ++i) + { + if (pPropName[i].getLength()) + xPropSet->addPropertyChangeListener( pPropName[i], this ); + } + } +} + +void PropertyChgHelper::RemoveAsPropListener() +{ + if (xPropSet.is()) + { + sal_Int32 nLen = aPropNames.getLength(); + const OUString *pPropName = aPropNames.getConstArray(); + for (sal_Int32 i = 0; i < nLen; ++i) + { + if (pPropName[i].getLength()) + xPropSet->removePropertyChangeListener( pPropName[i], this ); + } + } +} + + +void PropertyChgHelper::LaunchEvent( const LinguServiceEvent &rEvt ) +{ + cppu::OInterfaceIteratorHelper aIt( aLngSvcEvtListeners ); + while (aIt.hasMoreElements()) + { + Reference< XLinguServiceEventListener > xRef( aIt.next(), UNO_QUERY ); + if (xRef.is()) + xRef->processLinguServiceEvent( rEvt ); + } +} + + +void SAL_CALL PropertyChgHelper::disposing( const EventObject& rSource ) + throw(RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + if (rSource.Source == xPropSet) + { + RemoveAsPropListener(); + xPropSet = NULL; + aPropNames.realloc( 0 ); + } +} + + +sal_Bool SAL_CALL + PropertyChgHelper::addLinguServiceEventListener( + const Reference< XLinguServiceEventListener >& rxListener ) + throw(RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + + sal_Bool bRes = sal_False; + if (rxListener.is()) + { + sal_Int32 nCount = aLngSvcEvtListeners.getLength(); + bRes = aLngSvcEvtListeners.addInterface( rxListener ) != nCount; + } + return bRes; +} + + +sal_Bool SAL_CALL + PropertyChgHelper::removeLinguServiceEventListener( + const Reference< XLinguServiceEventListener >& rxListener ) + throw(RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + + sal_Bool bRes = sal_False; + if (rxListener.is()) + { + sal_Int32 nCount = aLngSvcEvtListeners.getLength(); + bRes = aLngSvcEvtListeners.removeInterface( rxListener ) != nCount; + } + return bRes; +} + + + +PropertyHelper_Thes::PropertyHelper_Thes( + const Reference< XInterface > &rxSource, + Reference< XPropertySet > &rxPropSet ) : + PropertyChgHelper ( rxSource, rxPropSet, 0 ) +{ + SetDefaultValues(); + GetCurrentValues(); +} + + +PropertyHelper_Thes::~PropertyHelper_Thes() +{ +} + + +void SAL_CALL + PropertyHelper_Thes::propertyChange( const PropertyChangeEvent& rEvt ) + throw(RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + PropertyChgHelper::propertyChange_Impl( rEvt ); +} + + + +// list of properties from the property set to be used +// and listened to +static const char *aSP[] = +{ + UPN_IS_SPELL_UPPER_CASE, + UPN_IS_SPELL_WITH_DIGITS, + UPN_IS_SPELL_CAPITALIZATION +}; + + +PropertyHelper_Spell::PropertyHelper_Spell( + const Reference< XInterface > & rxSource, + Reference< XPropertySet > &rxPropSet ) : + PropertyChgHelper ( rxSource, rxPropSet, AE_SPELLCHECKER ) +{ + AddPropNames( aSP, SAL_N_ELEMENTS(aSP)); + SetDefaultValues(); + GetCurrentValues(); + + nResMaxNumberOfSuggestions = GetDefaultNumberOfSuggestions(); +} + + +PropertyHelper_Spell::~PropertyHelper_Spell() +{ +} + + +void PropertyHelper_Spell::SetDefaultValues() +{ + PropertyChgHelper::SetDefaultValues(); + + bResIsSpellUpperCase = bIsSpellUpperCase = sal_False; + bResIsSpellWithDigits = bIsSpellWithDigits = sal_False; + bResIsSpellCapitalization = bIsSpellCapitalization = sal_True; +} + + +void PropertyHelper_Spell::GetCurrentValues() +{ + PropertyChgHelper::GetCurrentValues(); + + sal_Int32 nLen = GetPropNames().getLength(); + if (GetPropSet().is() && nLen) + { + const OUString *pPropName = GetPropNames().getConstArray(); + for (sal_Int32 i = 0; i < nLen; ++i) + { + sal_Bool *pbVal = NULL, + *pbResVal = NULL; + + if (pPropName[i].equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( UPN_IS_SPELL_UPPER_CASE ) )) + { + pbVal = &bIsSpellUpperCase; + pbResVal = &bResIsSpellUpperCase; + } + else if (pPropName[i].equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( UPN_IS_SPELL_WITH_DIGITS ) )) + { + pbVal = &bIsSpellWithDigits; + pbResVal = &bResIsSpellWithDigits; + } + else if (pPropName[i].equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( UPN_IS_SPELL_CAPITALIZATION ) )) + { + pbVal = &bIsSpellCapitalization; + pbResVal = &bResIsSpellCapitalization; + } + + if (pbVal && pbResVal) + { + GetPropSet()->getPropertyValue( pPropName[i] ) >>= *pbVal; + *pbResVal = *pbVal; + } + } + } +} + + +sal_Bool PropertyHelper_Spell::propertyChange_Impl( const PropertyChangeEvent& rEvt ) +{ + sal_Bool bRes = PropertyChgHelper::propertyChange_Impl( rEvt ); + + if (!bRes && GetPropSet().is() && rEvt.Source == GetPropSet()) + { + sal_Bool bSCWA = sal_False, // SPELL_CORRECT_WORDS_AGAIN ? + bSWWA = sal_False; // SPELL_WRONG_WORDS_AGAIN ? + + sal_Bool *pbVal = NULL; + switch (rEvt.PropertyHandle) + { + case UPH_IS_SPELL_UPPER_CASE : + { + pbVal = &bIsSpellUpperCase; + bSCWA = sal_False == *pbVal; // sal_False->sal_True change? + bSWWA = !bSCWA; // sal_True->sal_False change? + break; + } + case UPH_IS_SPELL_WITH_DIGITS : + { + pbVal = &bIsSpellWithDigits; + bSCWA = sal_False == *pbVal; // sal_False->sal_True change? + bSWWA = !bSCWA; // sal_True->sal_False change? + break; + } + case UPH_IS_SPELL_CAPITALIZATION : + { + pbVal = &bIsSpellCapitalization; + bSCWA = sal_False == *pbVal; // sal_False->sal_True change? + bSWWA = !bSCWA; // sal_True->sal_False change? + break; + } + default: + DBG_ASSERT( 0, "unknown property" ); + } + if (pbVal) + rEvt.NewValue >>= *pbVal; + + bRes = (pbVal != 0); + if (bRes) + { + sal_Int16 nLngSvcFlags = 0; + if (bSCWA) + nLngSvcFlags |= LinguServiceEventFlags::SPELL_CORRECT_WORDS_AGAIN; + if (bSWWA) + nLngSvcFlags |= LinguServiceEventFlags::SPELL_WRONG_WORDS_AGAIN; + if (nLngSvcFlags) + { + LinguServiceEvent aEvt( GetEvtObj(), nLngSvcFlags ); + LaunchEvent( aEvt ); + } + } + } + + return bRes; +} + + +void SAL_CALL + PropertyHelper_Spell::propertyChange( const PropertyChangeEvent& rEvt ) + throw(RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + propertyChange_Impl( rEvt ); +} + + +void PropertyHelper_Spell::SetTmpPropVals( const PropertyValues &rPropVals ) +{ + PropertyChgHelper::SetTmpPropVals( rPropVals ); + + // return value is default value unless there is an explicitly supplied + // temporary value + nResMaxNumberOfSuggestions = GetDefaultNumberOfSuggestions(); + bResIsSpellWithDigits = bIsSpellWithDigits; + bResIsSpellCapitalization = bIsSpellCapitalization; + + sal_Int32 nLen = rPropVals.getLength(); + if (nLen) + { + const PropertyValue *pVal = rPropVals.getConstArray(); + for (sal_Int32 i = 0; i < nLen; ++i) + { + if (pVal[i].Name.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM(UPN_MAX_NUMBER_OF_SUGGESTIONS))) + { + pVal[i].Value >>= nResMaxNumberOfSuggestions; + } + else + { + sal_Bool *pbResVal = NULL; + switch (pVal[i].Handle) + { + case UPH_IS_SPELL_UPPER_CASE : pbResVal = &bResIsSpellUpperCase; break; + case UPH_IS_SPELL_WITH_DIGITS : pbResVal = &bResIsSpellWithDigits; break; + case UPH_IS_SPELL_CAPITALIZATION : pbResVal = &bResIsSpellCapitalization; break; + default: + DBG_ASSERT( 0, "unknown property" ); + } + if (pbResVal) + pVal[i].Value >>= *pbResVal; + } + } + } +} + +sal_Int16 PropertyHelper_Spell::GetDefaultNumberOfSuggestions() const +{ + return 16; +} + + +static const char *aHP[] = +{ + UPN_HYPH_MIN_LEADING, + UPN_HYPH_MIN_TRAILING, + UPN_HYPH_MIN_WORD_LENGTH +}; + + +PropertyHelper_Hyphen::PropertyHelper_Hyphen( + const Reference< XInterface > & rxSource, + Reference< XPropertySet > &rxPropSet ) : + PropertyChgHelper ( rxSource, rxPropSet, AE_HYPHENATOR ) +{ + AddPropNames( aHP, SAL_N_ELEMENTS(aHP)); + SetDefaultValues(); + GetCurrentValues(); +} + + +PropertyHelper_Hyphen::~PropertyHelper_Hyphen() +{ +} + + +void PropertyHelper_Hyphen::SetDefaultValues() +{ + PropertyChgHelper::SetDefaultValues(); + + nResHyphMinLeading = nHyphMinLeading = 2; + nResHyphMinTrailing = nHyphMinTrailing = 2; + nResHyphMinWordLength = nHyphMinWordLength = 0; +} + + +void PropertyHelper_Hyphen::GetCurrentValues() +{ + PropertyChgHelper::GetCurrentValues(); + + sal_Int32 nLen = GetPropNames().getLength(); + if (GetPropSet().is() && nLen) + { + const OUString *pPropName = GetPropNames().getConstArray(); + for (sal_Int32 i = 0; i < nLen; ++i) + { + sal_Int16 *pnVal = NULL, + *pnResVal = NULL; + + if (pPropName[i].equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( UPN_HYPH_MIN_LEADING ) )) + { + pnVal = &nHyphMinLeading; + pnResVal = &nResHyphMinLeading; + } + else if (pPropName[i].equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( UPN_HYPH_MIN_TRAILING ) )) + { + pnVal = &nHyphMinTrailing; + pnResVal = &nResHyphMinTrailing; + } + else if (pPropName[i].equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( UPN_HYPH_MIN_WORD_LENGTH ) )) + { + pnVal = &nHyphMinWordLength; + pnResVal = &nResHyphMinWordLength; + } + + if (pnVal && pnResVal) + { + GetPropSet()->getPropertyValue( pPropName[i] ) >>= *pnVal; + *pnResVal = *pnVal; + } + } + } +} + + +sal_Bool PropertyHelper_Hyphen::propertyChange_Impl( const PropertyChangeEvent& rEvt ) +{ + sal_Bool bRes = PropertyChgHelper::propertyChange_Impl( rEvt ); + + if (!bRes && GetPropSet().is() && rEvt.Source == GetPropSet()) + { + sal_Int16 nLngSvcFlags = LinguServiceEventFlags::HYPHENATE_AGAIN; + + sal_Int16 *pnVal = NULL; + switch (rEvt.PropertyHandle) + { + case UPH_HYPH_MIN_LEADING : pnVal = &nHyphMinLeading; break; + case UPH_HYPH_MIN_TRAILING : pnVal = &nHyphMinTrailing; break; + case UPH_HYPH_MIN_WORD_LENGTH : pnVal = &nHyphMinWordLength; break; + default: + DBG_ASSERT( 0, "unknown property" ); + } + if (pnVal) + rEvt.NewValue >>= *pnVal; + + bRes = (pnVal != 0); + if (bRes) + { + if (nLngSvcFlags) + { + LinguServiceEvent aEvt( GetEvtObj(), nLngSvcFlags ); + LaunchEvent( aEvt ); + } + } + } + + return bRes; +} + + +void SAL_CALL + PropertyHelper_Hyphen::propertyChange( const PropertyChangeEvent& rEvt ) + throw(RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + propertyChange_Impl( rEvt ); +} + + +void PropertyHelper_Hyphen::SetTmpPropVals( const PropertyValues &rPropVals ) +{ + PropertyChgHelper::SetTmpPropVals( rPropVals ); + + // return value is default value unless there is an explicitly supplied + // temporary value + nResHyphMinLeading = nHyphMinLeading; + nResHyphMinTrailing = nHyphMinTrailing; + nResHyphMinWordLength = nHyphMinWordLength; + + sal_Int32 nLen = rPropVals.getLength(); + if (nLen) + { + const PropertyValue *pVal = rPropVals.getConstArray(); + for (sal_Int32 i = 0; i < nLen; ++i) + { + sal_Int16 *pnResVal = NULL; + switch (pVal[i].Handle) + { + case UPH_HYPH_MIN_LEADING : pnResVal = &nResHyphMinLeading; break; + case UPH_HYPH_MIN_TRAILING : pnResVal = &nResHyphMinTrailing; break; + case UPH_HYPH_MIN_WORD_LENGTH : pnResVal = &nResHyphMinWordLength; break; + default: + DBG_ASSERT( 0, "unknown property" ); + } + if (pnResVal) + pVal[i].Value >>= *pnResVal; + } + } +} + +PropertyHelper_Thesaurus::PropertyHelper_Thesaurus( + const ::com::sun::star::uno::Reference< + ::com::sun::star::uno::XInterface > &rxSource, + ::com::sun::star::uno::Reference< + ::com::sun::star::beans::XPropertySet > &rxPropSet ) +{ + pInst = new PropertyHelper_Thes( rxSource, rxPropSet ); + xPropHelper = pInst; +} + +PropertyHelper_Thesaurus::~PropertyHelper_Thesaurus() +{ +} + +void PropertyHelper_Thesaurus::AddAsPropListener() +{ + pInst->AddAsPropListener(); +} + +void PropertyHelper_Thesaurus::RemoveAsPropListener() +{ + pInst->RemoveAsPropListener(); +} + +void PropertyHelper_Thesaurus::SetTmpPropVals( const com::sun::star::beans::PropertyValues &rPropVals ) +{ + pInst->SetTmpPropVals( rPropVals ); +} + +PropertyHelper_Hyphenation::PropertyHelper_Hyphenation( + const ::com::sun::star::uno::Reference< + ::com::sun::star::uno::XInterface > &rxSource, + ::com::sun::star::uno::Reference< + ::com::sun::star::beans::XPropertySet > &rxPropSet) +{ + pInst = new PropertyHelper_Hyphen( rxSource, rxPropSet ); + xPropHelper = pInst; +} + +PropertyHelper_Hyphenation::~PropertyHelper_Hyphenation() +{ +} + +void PropertyHelper_Hyphenation::AddAsPropListener() +{ + pInst->AddAsPropListener(); +} + +void PropertyHelper_Hyphenation::RemoveAsPropListener() +{ + pInst->RemoveAsPropListener(); +} + +void PropertyHelper_Hyphenation::SetTmpPropVals( const com::sun::star::beans::PropertyValues &rPropVals ) +{ + pInst->SetTmpPropVals( rPropVals ); +} + +sal_Int16 PropertyHelper_Hyphenation::GetMinLeading() const +{ + return pInst->GetMinLeading(); +} + +sal_Int16 PropertyHelper_Hyphenation::GetMinTrailing() const +{ + return pInst->GetMinTrailing(); +} + +sal_Int16 PropertyHelper_Hyphenation::GetMinWordLength() const +{ + return pInst->GetMinWordLength(); +} + +sal_Bool PropertyHelper_Hyphenation::addLinguServiceEventListener( + const ::com::sun::star::uno::Reference< + ::com::sun::star::linguistic2::XLinguServiceEventListener >& rxListener ) + throw(::com::sun::star::uno::RuntimeException) +{ + return pInst->addLinguServiceEventListener( rxListener ); +} + +sal_Bool PropertyHelper_Hyphenation::removeLinguServiceEventListener( + const ::com::sun::star::uno::Reference< + ::com::sun::star::linguistic2::XLinguServiceEventListener >& rxListener ) + throw(::com::sun::star::uno::RuntimeException) +{ + return pInst->removeLinguServiceEventListener( rxListener ); +} + +PropertyHelper_Spelling::PropertyHelper_Spelling( + const ::com::sun::star::uno::Reference< + ::com::sun::star::uno::XInterface > &rxSource, + ::com::sun::star::uno::Reference< + ::com::sun::star::beans::XPropertySet > &rxPropSet ) +{ + pInst = new PropertyHelper_Spell( rxSource, rxPropSet ); + xPropHelper = pInst; +} + +PropertyHelper_Spelling::~PropertyHelper_Spelling() +{ +} + +void PropertyHelper_Spelling::AddAsPropListener() +{ + pInst->AddAsPropListener(); +} + +void PropertyHelper_Spelling::RemoveAsPropListener() +{ + pInst->RemoveAsPropListener(); +} + +void PropertyHelper_Spelling::SetTmpPropVals( const com::sun::star::beans::PropertyValues &rPropVals ) +{ + pInst->SetTmpPropVals( rPropVals ); +} + +sal_Bool PropertyHelper_Spelling::IsSpellUpperCase() const +{ + return pInst->IsSpellUpperCase(); +} + +sal_Bool PropertyHelper_Spelling::IsSpellWithDigits() const +{ + return pInst->IsSpellWithDigits(); +} + +sal_Bool PropertyHelper_Spelling::IsSpellCapitalization() const +{ + return pInst->IsSpellCapitalization(); +} + +sal_Bool PropertyHelper_Spelling::addLinguServiceEventListener( + const ::com::sun::star::uno::Reference< + ::com::sun::star::linguistic2::XLinguServiceEventListener >& rxListener ) + throw(::com::sun::star::uno::RuntimeException) +{ + return pInst->addLinguServiceEventListener( rxListener ); +} + +sal_Bool PropertyHelper_Spelling::removeLinguServiceEventListener( + const ::com::sun::star::uno::Reference< + ::com::sun::star::linguistic2::XLinguServiceEventListener >& rxListener ) + throw(::com::sun::star::uno::RuntimeException) +{ + return pInst->removeLinguServiceEventListener( rxListener ); +} + +} // namespace linguistic + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/linguistic/source/lngreg.cxx b/linguistic/source/lngreg.cxx new file mode 100644 index 000000000000..997efef110a4 --- /dev/null +++ b/linguistic/source/lngreg.cxx @@ -0,0 +1,118 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_linguistic.hxx" + + +#include <cppuhelper/factory.hxx> // helper for factories +#include <rtl/string.hxx> + +#include <com/sun/star/registry/XRegistryKey.hpp> + +using namespace com::sun::star::lang; + +using namespace com::sun::star::registry; + +extern void * SAL_CALL LngSvcMgr_getFactory +( + const sal_Char * pImplName, + XMultiServiceFactory * pServiceManager, + void * /*pRegistryKey*/ +); + +extern void * SAL_CALL DicList_getFactory +( + const sal_Char * pImplName, + XMultiServiceFactory * pServiceManager, + void * +); + +void * SAL_CALL LinguProps_getFactory +( + const sal_Char * pImplName, + XMultiServiceFactory * pServiceManager, + void * +); + +extern void * SAL_CALL ConvDicList_getFactory +( + const sal_Char * pImplName, + XMultiServiceFactory * pServiceManager, + void * +); + +extern void * SAL_CALL GrammarCheckingIterator_getFactory +( + const sal_Char * pImplName, + XMultiServiceFactory * pServiceManager, + void * +); + + +extern "C" +{ + +SAL_DLLPUBLIC_EXPORT void * SAL_CALL lng_component_getFactory( + const sal_Char * pImplName, void * pServiceManager, void * pRegistryKey ) +{ + void * pRet = + LngSvcMgr_getFactory( + pImplName, + reinterpret_cast< XMultiServiceFactory * >( pServiceManager ), + pRegistryKey ); + + if(!pRet) + pRet = LinguProps_getFactory( + pImplName, + reinterpret_cast< XMultiServiceFactory * >( pServiceManager ), + pRegistryKey ); + + if(!pRet) + pRet = DicList_getFactory( + pImplName, + reinterpret_cast< XMultiServiceFactory * >( pServiceManager ), + pRegistryKey ); + + if(!pRet) + pRet = ConvDicList_getFactory( + pImplName, + reinterpret_cast< XMultiServiceFactory * >( pServiceManager ), + pRegistryKey ); + + if(!pRet) + pRet = GrammarCheckingIterator_getFactory( + pImplName, + reinterpret_cast< XMultiServiceFactory * >( pServiceManager ), + pRegistryKey ); + return pRet; +} +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/linguistic/source/lngsvcmgr.cxx b/linguistic/source/lngsvcmgr.cxx new file mode 100644 index 000000000000..0893c0719436 --- /dev/null +++ b/linguistic/source/lngsvcmgr.cxx @@ -0,0 +1,1862 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_linguistic.hxx" + +#include <com/sun/star/registry/XRegistryKey.hpp> +#include <com/sun/star/container/XContentEnumerationAccess.hpp> +#include <com/sun/star/container/XEnumeration.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/linguistic2/XSupportedLocales.hpp> +#include <com/sun/star/linguistic2/DictionaryListEventFlags.hpp> +#include <com/sun/star/linguistic2/LinguServiceEventFlags.hpp> + +#include <tools/solar.h> +#include <unotools/lingucfg.hxx> +#include <unotools/processfactory.hxx> +#include <i18npool/lang.h> +#include <i18npool/mslangid.hxx> +#include <cppuhelper/factory.hxx> +#include <comphelper/extract.hxx> +#include <rtl/logfile.hxx> + +#include "lngsvcmgr.hxx" +#include "lngopt.hxx" +#include "linguistic/misc.hxx" +#include "spelldsp.hxx" +#include "hyphdsp.hxx" +#include "thesdsp.hxx" +#include "gciterator.hxx" + + +using namespace com::sun::star; +using namespace linguistic; +using ::rtl::OUString; + +// forward declarations +uno::Sequence< OUString > static GetLangSvcList( const uno::Any &rVal ); +uno::Sequence< OUString > static GetLangSvc( const uno::Any &rVal ); + + +static sal_Bool lcl_SeqHasString( const uno::Sequence< OUString > &rSeq, const OUString &rText ) +{ + sal_Bool bRes = sal_False; + + sal_Int32 nLen = rSeq.getLength(); + if (nLen == 0 || rText.getLength() == 0) + return bRes; + + const OUString *pSeq = rSeq.getConstArray(); + for (sal_Int32 i = 0; i < nLen && !bRes; ++i) + { + if (rText == pSeq[i]) + bRes = sal_True; + } + return bRes; +} + + +static uno::Sequence< lang::Locale > GetAvailLocales( + const uno::Sequence< OUString > &rSvcImplNames ) +{ + uno::Sequence< lang::Locale > aRes; + + uno::Reference< lang::XMultiServiceFactory > xFac( utl::getProcessServiceFactory() ); + sal_Int32 nNames = rSvcImplNames.getLength(); + if (nNames && xFac.is()) + { + std::set< LanguageType > aLanguages; + + //! since we're going to create one-instance services we have to + //! supply their arguments even if we would not need them here... + uno::Sequence< uno::Any > aArgs(2); + aArgs.getArray()[0] <<= GetLinguProperties(); + + // check all services for the supported languages and new + // languages to the result + const OUString *pImplNames = rSvcImplNames.getConstArray(); + sal_Int32 i; + + for (i = 0; i < nNames; ++i) + { + uno::Reference< linguistic2::XSupportedLocales > xSuppLoc; + try + { + xSuppLoc = uno::Reference< linguistic2::XSupportedLocales >( + xFac->createInstanceWithArguments( pImplNames[i], aArgs ), uno::UNO_QUERY ); + } + catch (uno::Exception &) + { + DBG_ASSERT( 0, "createInstanceWithArguments failed" ); + } + + if (xSuppLoc.is()) + { + uno::Sequence< lang::Locale > aLoc( xSuppLoc->getLocales() ); + sal_Int32 nLoc = aLoc.getLength(); + for (sal_Int32 k = 0; k < nLoc; ++k) + { + const lang::Locale *pLoc = aLoc.getConstArray(); + LanguageType nLang = LocaleToLanguage( pLoc[k] ); + + // language not already added? + if (aLanguages.find( nLang ) == aLanguages.end()) + aLanguages.insert( nLang ); + } + } + else + { + DBG_ASSERT( 0, "interface not supported by service" ); + } + } + + // build return sequence + sal_Int32 nLanguages = static_cast< sal_Int32 >(aLanguages.size()); + aRes.realloc( nLanguages ); + lang::Locale *pRes = aRes.getArray(); + std::set< LanguageType >::const_iterator aIt( aLanguages.begin() ); + for (i = 0; aIt != aLanguages.end(); ++aIt, ++i) + { + LanguageType nLang = *aIt; + pRes[i] = CreateLocale( nLang ); + } + } + + return aRes; +} + + +struct SvcInfo +{ + const OUString aSvcImplName; + const uno::Sequence< sal_Int16 > aSuppLanguages; + + SvcInfo( const OUString &rSvcImplName, + const uno::Sequence< sal_Int16 > &rSuppLanguages ) : + aSvcImplName (rSvcImplName), + aSuppLanguages (rSuppLanguages) + { + } + + sal_Bool HasLanguage( sal_Int16 nLanguage ) const; +}; + + +sal_Bool SvcInfo::HasLanguage( sal_Int16 nLanguage ) const +{ + sal_Int32 nCnt = aSuppLanguages.getLength(); + const sal_Int16 *pLang = aSuppLanguages.getConstArray(); + sal_Int32 i; + + for ( i = 0; i < nCnt; ++i) + { + if (nLanguage == pLang[i]) + break; + } + return i < nCnt; +} + +class LngSvcMgrListenerHelper : + public cppu::WeakImplHelper2 + < + linguistic2::XLinguServiceEventListener, + linguistic2::XDictionaryListEventListener + > +{ + LngSvcMgr &rMyManager; + + ::cppu::OInterfaceContainerHelper aLngSvcMgrListeners; + ::cppu::OInterfaceContainerHelper aLngSvcEvtBroadcasters; + uno::Reference< linguistic2::XDictionaryList > xDicList; + + sal_Int16 nCombinedLngSvcEvt; + + // disallow copy-constructor and assignment-operator for now + LngSvcMgrListenerHelper(const LngSvcMgrListenerHelper &); + LngSvcMgrListenerHelper & operator = (const LngSvcMgrListenerHelper &); + + void LaunchEvent( sal_Int16 nLngSvcEvtFlags ); + + long Timeout(); + +public: + LngSvcMgrListenerHelper( LngSvcMgr &rLngSvcMgr, + const uno::Reference< linguistic2::XDictionaryList > &rxDicList ); + + // lang::XEventListener + virtual void SAL_CALL + disposing( const lang::EventObject& rSource ) + throw(uno::RuntimeException); + + // linguistic2::XLinguServiceEventListener + virtual void SAL_CALL + processLinguServiceEvent( const linguistic2::LinguServiceEvent& aLngSvcEvent ) + throw(uno::RuntimeException); + + // linguistic2::XDictionaryListEventListener + virtual void SAL_CALL + processDictionaryListEvent( + const linguistic2::DictionaryListEvent& rDicListEvent ) + throw(uno::RuntimeException); + + inline sal_Bool AddLngSvcMgrListener( + const uno::Reference< lang::XEventListener >& rxListener ); + inline sal_Bool RemoveLngSvcMgrListener( + const uno::Reference< lang::XEventListener >& rxListener ); + void DisposeAndClear( const lang::EventObject &rEvtObj ); + sal_Bool AddLngSvcEvtBroadcaster( + const uno::Reference< linguistic2::XLinguServiceEventBroadcaster > &rxBroadcaster ); + sal_Bool RemoveLngSvcEvtBroadcaster( + const uno::Reference< linguistic2::XLinguServiceEventBroadcaster > &rxBroadcaster ); + + void AddLngSvcEvt( sal_Int16 nLngSvcEvt ); +}; + + +LngSvcMgrListenerHelper::LngSvcMgrListenerHelper( + LngSvcMgr &rLngSvcMgr, + const uno::Reference< linguistic2::XDictionaryList > &rxDicList ) : + rMyManager ( rLngSvcMgr ), + aLngSvcMgrListeners ( GetLinguMutex() ), + aLngSvcEvtBroadcasters ( GetLinguMutex() ), + xDicList ( rxDicList ) +{ + if (xDicList.is()) + { + xDicList->addDictionaryListEventListener( + (linguistic2::XDictionaryListEventListener *) this, sal_False ); + } + + nCombinedLngSvcEvt = 0; +} + + +void SAL_CALL LngSvcMgrListenerHelper::disposing( const lang::EventObject& rSource ) + throw(uno::RuntimeException) +{ + osl::MutexGuard aGuard( GetLinguMutex() ); + + uno::Reference< uno::XInterface > xRef( rSource.Source ); + if ( xRef.is() ) + { + aLngSvcMgrListeners .removeInterface( xRef ); + aLngSvcEvtBroadcasters.removeInterface( xRef ); + if (xDicList == xRef) + xDicList = 0; + } +} + + +//IMPL_LINK( LngSvcMgrListenerHelper, TimeOut, Timer*, pTimer ) +long LngSvcMgrListenerHelper::Timeout() +{ + osl::MutexGuard aGuard( GetLinguMutex() ); + + { + // change event source to LinguServiceManager since the listeners + // probably do not know (and need not to know) about the specific + // SpellChecker's or Hyphenator's. + linguistic2::LinguServiceEvent aEvtObj( + static_cast<com::sun::star::linguistic2::XLinguServiceManager*>(&rMyManager), nCombinedLngSvcEvt ); + nCombinedLngSvcEvt = 0; + + if (rMyManager.pSpellDsp) + rMyManager.pSpellDsp->FlushSpellCache(); + + // pass event on to linguistic2::XLinguServiceEventListener's + cppu::OInterfaceIteratorHelper aIt( aLngSvcMgrListeners ); + while (aIt.hasMoreElements()) + { + uno::Reference< linguistic2::XLinguServiceEventListener > xRef( aIt.next(), uno::UNO_QUERY ); + if (xRef.is()) + xRef->processLinguServiceEvent( aEvtObj ); + } + } + return 0; +} + + +void LngSvcMgrListenerHelper::AddLngSvcEvt( sal_Int16 nLngSvcEvt ) +{ + nCombinedLngSvcEvt |= nLngSvcEvt; + Timeout(); +} + + +void SAL_CALL + LngSvcMgrListenerHelper::processLinguServiceEvent( + const linguistic2::LinguServiceEvent& rLngSvcEvent ) + throw(uno::RuntimeException) +{ + osl::MutexGuard aGuard( GetLinguMutex() ); + AddLngSvcEvt( rLngSvcEvent.nEvent ); +} + + +void SAL_CALL + LngSvcMgrListenerHelper::processDictionaryListEvent( + const linguistic2::DictionaryListEvent& rDicListEvent ) + throw(uno::RuntimeException) +{ + osl::MutexGuard aGuard( GetLinguMutex() ); + + sal_Int16 nDlEvt = rDicListEvent.nCondensedEvent; + if (0 == nDlEvt) + return; + + // we do keep the original event source here though... + + // pass event on to linguistic2::XDictionaryListEventListener's + cppu::OInterfaceIteratorHelper aIt( aLngSvcMgrListeners ); + while (aIt.hasMoreElements()) + { + uno::Reference< linguistic2::XDictionaryListEventListener > xRef( aIt.next(), uno::UNO_QUERY ); + if (xRef.is()) + xRef->processDictionaryListEvent( rDicListEvent ); + } + + // "translate" DictionaryList event into linguistic2::LinguServiceEvent + sal_Int16 nLngSvcEvt = 0; + sal_Int16 nSpellCorrectFlags = + linguistic2::DictionaryListEventFlags::ADD_NEG_ENTRY | + linguistic2::DictionaryListEventFlags::DEL_POS_ENTRY | + linguistic2::DictionaryListEventFlags::ACTIVATE_NEG_DIC | + linguistic2::DictionaryListEventFlags::DEACTIVATE_POS_DIC; + if (0 != (nDlEvt & nSpellCorrectFlags)) + nLngSvcEvt |= linguistic2::LinguServiceEventFlags::SPELL_CORRECT_WORDS_AGAIN; + + sal_Int16 nSpellWrongFlags = + linguistic2::DictionaryListEventFlags::ADD_POS_ENTRY | + linguistic2::DictionaryListEventFlags::DEL_NEG_ENTRY | + linguistic2::DictionaryListEventFlags::ACTIVATE_POS_DIC | + linguistic2::DictionaryListEventFlags::DEACTIVATE_NEG_DIC; + if (0 != (nDlEvt & nSpellWrongFlags)) + nLngSvcEvt |= linguistic2::LinguServiceEventFlags::SPELL_WRONG_WORDS_AGAIN; + + sal_Int16 nHyphenateFlags = + linguistic2::DictionaryListEventFlags::ADD_POS_ENTRY | + linguistic2::DictionaryListEventFlags::DEL_POS_ENTRY | + linguistic2::DictionaryListEventFlags::ACTIVATE_POS_DIC | + linguistic2::DictionaryListEventFlags::ACTIVATE_NEG_DIC; + if (0 != (nDlEvt & nHyphenateFlags)) + nLngSvcEvt |= linguistic2::LinguServiceEventFlags::HYPHENATE_AGAIN; + + if (rMyManager.pSpellDsp) + rMyManager.pSpellDsp->FlushSpellCache(); + if (nLngSvcEvt) + LaunchEvent( nLngSvcEvt ); +} + + +void LngSvcMgrListenerHelper::LaunchEvent( sal_Int16 nLngSvcEvtFlags ) +{ + linguistic2::LinguServiceEvent aEvt( + static_cast<com::sun::star::linguistic2::XLinguServiceManager*>(&rMyManager), nLngSvcEvtFlags ); + + // pass event on to linguistic2::XLinguServiceEventListener's + cppu::OInterfaceIteratorHelper aIt( aLngSvcMgrListeners ); + while (aIt.hasMoreElements()) + { + uno::Reference< linguistic2::XLinguServiceEventListener > xRef( aIt.next(), uno::UNO_QUERY ); + if (xRef.is()) + xRef->processLinguServiceEvent( aEvt ); + } +} + + +inline sal_Bool LngSvcMgrListenerHelper::AddLngSvcMgrListener( + const uno::Reference< lang::XEventListener >& rxListener ) +{ + aLngSvcMgrListeners.addInterface( rxListener ); + return sal_True; +} + + +inline sal_Bool LngSvcMgrListenerHelper::RemoveLngSvcMgrListener( + const uno::Reference< lang::XEventListener >& rxListener ) +{ + aLngSvcMgrListeners.removeInterface( rxListener ); + return sal_True; +} + + +void LngSvcMgrListenerHelper::DisposeAndClear( const lang::EventObject &rEvtObj ) +{ + // call "disposing" for all listeners and clear list + aLngSvcMgrListeners .disposeAndClear( rEvtObj ); + + // remove references to this object hold by the broadcasters + cppu::OInterfaceIteratorHelper aIt( aLngSvcEvtBroadcasters ); + while (aIt.hasMoreElements()) + { + uno::Reference< linguistic2::XLinguServiceEventBroadcaster > xRef( aIt.next(), uno::UNO_QUERY ); + if (xRef.is()) + RemoveLngSvcEvtBroadcaster( xRef ); + } + + // remove refernce to this object hold by the dictionary-list + if (xDicList.is()) + { + xDicList->removeDictionaryListEventListener( + (linguistic2::XDictionaryListEventListener *) this ); + xDicList = 0; + } +} + + +sal_Bool LngSvcMgrListenerHelper::AddLngSvcEvtBroadcaster( + const uno::Reference< linguistic2::XLinguServiceEventBroadcaster > &rxBroadcaster ) +{ + sal_Bool bRes = sal_False; + if (rxBroadcaster.is()) + { + aLngSvcEvtBroadcasters.addInterface( rxBroadcaster ); + rxBroadcaster->addLinguServiceEventListener( + (linguistic2::XLinguServiceEventListener *) this ); + } + return bRes; +} + + +sal_Bool LngSvcMgrListenerHelper::RemoveLngSvcEvtBroadcaster( + const uno::Reference< linguistic2::XLinguServiceEventBroadcaster > &rxBroadcaster ) +{ + sal_Bool bRes = sal_False; + if (rxBroadcaster.is()) + { + aLngSvcEvtBroadcasters.removeInterface( rxBroadcaster ); + rxBroadcaster->removeLinguServiceEventListener( + (linguistic2::XLinguServiceEventListener *) this ); + } + return bRes; +} + + + + +LngSvcMgr::LngSvcMgr() + : utl::ConfigItem(OUString(RTL_CONSTASCII_USTRINGPARAM("Office.Linguistic"))) + , aEvtListeners(GetLinguMutex()) +{ + bDisposing = sal_False; + + pSpellDsp = 0; + pGrammarDsp = 0; + pHyphDsp = 0; + pThesDsp = 0; + + pAvailSpellSvcs = 0; + pAvailGrammarSvcs = 0; + pAvailHyphSvcs = 0; + pAvailThesSvcs = 0; + pListenerHelper = 0; + + // request notify events when properties (i.e. something in the subtree) changes + uno::Sequence< OUString > aNames(4); + OUString *pNames = aNames.getArray(); + pNames[0] = A2OU( "ServiceManager/SpellCheckerList" ); + pNames[1] = A2OU( "ServiceManager/GrammarCheckerList" ); + pNames[2] = A2OU( "ServiceManager/HyphenatorList" ); + pNames[3] = A2OU( "ServiceManager/ThesaurusList" ); + EnableNotification( aNames ); +} + + +LngSvcMgr::~LngSvcMgr() +{ + // memory for pSpellDsp, pHyphDsp, pThesDsp, pListenerHelper + // will be freed in the destructor of the respective Reference's + // xSpellDsp, xGrammarDsp, xHyphDsp, xThesDsp + + delete pAvailSpellSvcs; + delete pAvailGrammarSvcs; + delete pAvailHyphSvcs; + delete pAvailThesSvcs; +} + + +void LngSvcMgr::Notify( const uno::Sequence< OUString > &rPropertyNames ) +{ + const OUString aSpellCheckerList( A2OU("ServiceManager/SpellCheckerList") ); + const OUString aGrammarCheckerList( A2OU("ServiceManager/GrammarCheckerList") ); + const OUString aHyphenatorList( A2OU("ServiceManager/HyphenatorList") ); + const OUString aThesaurusList( A2OU("ServiceManager/ThesaurusList") ); + + const uno::Sequence< OUString > aSpellCheckerListEntries( GetNodeNames( aSpellCheckerList ) ); + const uno::Sequence< OUString > aGrammarCheckerListEntries( GetNodeNames( aGrammarCheckerList ) ); + const uno::Sequence< OUString > aHyphenatorListEntries( GetNodeNames( aHyphenatorList ) ); + const uno::Sequence< OUString > aThesaurusListEntries( GetNodeNames( aThesaurusList ) ); + + uno::Sequence< uno::Any > aValues; + uno::Sequence< OUString > aNames( 1 ); + OUString *pNames = aNames.getArray(); + + sal_Int32 nLen = rPropertyNames.getLength(); + const OUString *pPropertyNames = rPropertyNames.getConstArray(); + for (sal_Int32 i = 0; i < nLen; ++i) + { + // property names look like + // "ServiceManager/ThesaurusList/de-CH" + + const OUString &rName = pPropertyNames[i]; + sal_Int32 nKeyStart; + nKeyStart = rName.lastIndexOf( '/' ); + OUString aKeyText; + if (nKeyStart != -1) + aKeyText = rName.copy( nKeyStart + 1 ); + DBG_ASSERT( aKeyText.getLength() != 0, "unexpected key (lang::Locale) string" ); + if (0 == rName.compareTo( aSpellCheckerList, aSpellCheckerList.getLength() )) + { + // delete old cached data, needs to be acquired new on demand + delete pAvailSpellSvcs; pAvailSpellSvcs = 0; + + OUString aNode( aSpellCheckerList ); + if (lcl_SeqHasString( aSpellCheckerListEntries, aKeyText )) + { + OUString aPropName( aNode ); + aPropName += OUString::valueOf( (sal_Unicode) '/' ); + aPropName += aKeyText; + pNames[0] = aPropName; + aValues = /*aCfg.*/GetProperties( aNames ); + uno::Sequence< OUString > aSvcImplNames; + if (aValues.getLength()) + aSvcImplNames = GetLangSvcList( aValues.getConstArray()[0] ); + + LanguageType nLang = LANGUAGE_NONE; + if (0 != aKeyText.getLength()) + nLang = MsLangId::convertIsoStringToLanguage( aKeyText ); + + GetSpellCheckerDsp_Impl( sal_False ); // don't set service list, it will be done below + pSpellDsp->SetServiceList( CreateLocale(nLang), aSvcImplNames ); + } + } + else if (0 == rName.compareTo( aGrammarCheckerList, aGrammarCheckerList.getLength() )) + { + // delete old cached data, needs to be acquired new on demand + delete pAvailGrammarSvcs; pAvailGrammarSvcs = 0; + + OUString aNode( aGrammarCheckerList ); + if (lcl_SeqHasString( aGrammarCheckerListEntries, aKeyText )) + { + OUString aPropName( aNode ); + aPropName += OUString::valueOf( (sal_Unicode) '/' ); + aPropName += aKeyText; + pNames[0] = aPropName; + aValues = /*aCfg.*/GetProperties( aNames ); + uno::Sequence< OUString > aSvcImplNames; + if (aValues.getLength()) + aSvcImplNames = GetLangSvc( aValues.getConstArray()[0] ); + + LanguageType nLang = LANGUAGE_NONE; + if (0 != aKeyText.getLength()) + nLang = MsLangId::convertIsoStringToLanguage( aKeyText ); + + if (SvtLinguConfig().HasGrammarChecker()) + { + GetGrammarCheckerDsp_Impl( sal_False ); // don't set service list, it will be done below + pGrammarDsp->SetServiceList( CreateLocale(nLang), aSvcImplNames ); + } + } + } + else if (0 == rName.compareTo( aHyphenatorList, aHyphenatorList.getLength() )) + { + // delete old cached data, needs to be acquired new on demand + delete pAvailHyphSvcs; pAvailHyphSvcs = 0; + + OUString aNode( aHyphenatorList ); + if (lcl_SeqHasString( aHyphenatorListEntries, aKeyText )) + { + OUString aPropName( aNode ); + aPropName += OUString::valueOf( (sal_Unicode) '/' ); + aPropName += aKeyText; + pNames[0] = aPropName; + aValues = /*aCfg.*/GetProperties( aNames ); + uno::Sequence< OUString > aSvcImplNames; + if (aValues.getLength()) + aSvcImplNames = GetLangSvc( aValues.getConstArray()[0] ); + + LanguageType nLang = LANGUAGE_NONE; + if (0 != aKeyText.getLength()) + nLang = MsLangId::convertIsoStringToLanguage( aKeyText ); + + GetHyphenatorDsp_Impl( sal_False ); // don't set service list, it will be done below + pHyphDsp->SetServiceList( CreateLocale(nLang), aSvcImplNames ); + } + } + else if (0 == rName.compareTo( aThesaurusList, aThesaurusList.getLength() )) + { + // delete old cached data, needs to be acquired new on demand + delete pAvailThesSvcs; pAvailThesSvcs = 0; + + OUString aNode( aThesaurusList ); + if (lcl_SeqHasString( aThesaurusListEntries, aKeyText )) + { + OUString aPropName( aNode ); + aPropName += OUString::valueOf( (sal_Unicode) '/' ); + aPropName += aKeyText; + pNames[0] = aPropName; + aValues = /*aCfg.*/GetProperties( aNames ); + uno::Sequence< OUString > aSvcImplNames; + if (aValues.getLength()) + aSvcImplNames = GetLangSvcList( aValues.getConstArray()[0] ); + + LanguageType nLang = LANGUAGE_NONE; + if (0 != aKeyText.getLength()) + nLang = MsLangId::convertIsoStringToLanguage( aKeyText ); + + GetThesaurusDsp_Impl( sal_False ); // don't set service list, it will be done below + pThesDsp->SetServiceList( CreateLocale(nLang), aSvcImplNames ); + } + } + else + { + DBG_ASSERT( 0, "nofified for unexpected property" ); + } + } +} + + +void LngSvcMgr::Commit() +{ + // everything necessary should have already been done by 'SaveCfgSvcs' + // called from within 'setConfiguredServices'. + // Also this class usually exits only when the Office i sbeing shutdown. +} + + +void LngSvcMgr::GetListenerHelper_Impl() +{ + if (!pListenerHelper) + { + pListenerHelper = new LngSvcMgrListenerHelper( *this, linguistic::GetDictionaryList() ); + xListenerHelper = (linguistic2::XLinguServiceEventListener *) pListenerHelper; + } +} + + +void LngSvcMgr::GetSpellCheckerDsp_Impl( sal_Bool bSetSvcList ) +{ + if (!pSpellDsp) + { + pSpellDsp = new SpellCheckerDispatcher( *this ); + xSpellDsp = pSpellDsp; + if (bSetSvcList) + SetCfgServiceLists( *pSpellDsp ); + } +} + + +void LngSvcMgr::GetGrammarCheckerDsp_Impl( sal_Bool bSetSvcList ) +{ + if (!pGrammarDsp && SvtLinguConfig().HasGrammarChecker()) + { + //! since the grammar checking iterator needs to be a one instance service + //! we need to create it the correct way! + uno::Reference< linguistic2::XProofreadingIterator > xGCI; + try + { + uno::Reference< lang::XMultiServiceFactory > xMgr( + utl::getProcessServiceFactory(), uno::UNO_QUERY_THROW ); + xGCI = uno::Reference< linguistic2::XProofreadingIterator >( + xMgr->createInstance( A2OU( SN_GRAMMARCHECKINGITERATOR ) ), uno::UNO_QUERY_THROW ); + } + catch (uno::Exception &) + { + } + DBG_ASSERT( xGCI.is(), "instantiating grammar checking iterator failed" ); + + if (xGCI.is()) + { + pGrammarDsp = dynamic_cast< GrammarCheckingIterator * >(xGCI.get()); + xGrammarDsp = xGCI; + DBG_ASSERT( pGrammarDsp, "failed to get implementation" ); + if (bSetSvcList) + SetCfgServiceLists( *pGrammarDsp ); + } + } +} + + +void LngSvcMgr::GetHyphenatorDsp_Impl( sal_Bool bSetSvcList ) +{ + if (!pHyphDsp) + { + pHyphDsp = new HyphenatorDispatcher( *this ); + xHyphDsp = pHyphDsp; + if (bSetSvcList) + SetCfgServiceLists( *pHyphDsp ); + } +} + + +void LngSvcMgr::GetThesaurusDsp_Impl( sal_Bool bSetSvcList ) +{ + if (!pThesDsp) + { + pThesDsp = new ThesaurusDispatcher; + xThesDsp = pThesDsp; + if (bSetSvcList) + SetCfgServiceLists( *pThesDsp ); + } +} + + +void LngSvcMgr::GetAvailableSpellSvcs_Impl() +{ + if (!pAvailSpellSvcs) + { + pAvailSpellSvcs = new SvcInfoArray; + + uno::Reference< lang::XMultiServiceFactory > xFac( utl::getProcessServiceFactory() ); + if (xFac.is()) + { + uno::Reference< container::XContentEnumerationAccess > xEnumAccess( xFac, uno::UNO_QUERY ); + uno::Reference< container::XEnumeration > xEnum; + if (xEnumAccess.is()) + xEnum = xEnumAccess->createContentEnumeration( + A2OU( SN_SPELLCHECKER ) ); + + if (xEnum.is()) + { + while (xEnum->hasMoreElements()) + { + uno::Any aCurrent = xEnum->nextElement(); + uno::Reference< lang::XSingleComponentFactory > xCompFactory; + uno::Reference< lang::XSingleServiceFactory > xFactory; + + uno::Reference< linguistic2::XSpellChecker > xSvc; + if ( cppu::extractInterface( xCompFactory, aCurrent ) || ::cppu::extractInterface( xFactory, aCurrent ) ) + { + try + { + uno::Reference < uno::XComponentContext > xContext; + uno::Reference< beans::XPropertySet > xProps( xFac, uno::UNO_QUERY ); + + xProps->getPropertyValue( OUString( RTL_CONSTASCII_USTRINGPARAM( "DefaultContext" ))) >>= xContext; + xSvc = uno::Reference< linguistic2::XSpellChecker >( ( xCompFactory.is() ? xCompFactory->createInstanceWithContext( xContext ) : xFactory->createInstance() ), uno::UNO_QUERY ); + } + catch (uno::Exception &rEx) + { + (void) rEx; + DBG_ASSERT( 0, "createInstance failed" ); + } + } + + if (xSvc.is()) + { + OUString aImplName; + uno::Sequence< sal_Int16 > aLanguages; + 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()) { + uno::Sequence<lang::Locale> aLocaleSequence(xSuppLoc->getLocales()); + aLanguages = LocaleSeqToLangSeq( aLocaleSequence ); + } + + pAvailSpellSvcs->push_back( new SvcInfo( aImplName, aLanguages ) ); + } + } + } + } + } +} + + +void LngSvcMgr::GetAvailableGrammarSvcs_Impl() +{ + if (!pAvailGrammarSvcs) + { + pAvailGrammarSvcs = new SvcInfoArray; + + uno::Reference< lang::XMultiServiceFactory > xFac( utl::getProcessServiceFactory() ); + if (xFac.is()) + { + uno::Reference< container::XContentEnumerationAccess > xEnumAccess( xFac, 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< linguistic2::XProofreader > xSvc; + if ( cppu::extractInterface( xCompFactory, aCurrent ) || ::cppu::extractInterface( xFactory, aCurrent ) ) + { + try + { + uno::Reference < uno::XComponentContext > xContext; + uno::Reference< beans::XPropertySet > xProps( xFac, uno::UNO_QUERY ); + + xProps->getPropertyValue( OUString( RTL_CONSTASCII_USTRINGPARAM( "DefaultContext" ))) >>= xContext; + xSvc = uno::Reference< linguistic2::XProofreader >( ( xCompFactory.is() ? xCompFactory->createInstanceWithContext( xContext ) : xFactory->createInstance() ), uno::UNO_QUERY ); + } + catch (uno::Exception &rEx) + { + (void) rEx; + DBG_ASSERT( 0, "createInstance failed" ); + } + } + + if (xSvc.is()) + { + OUString aImplName; + uno::Sequence< sal_Int16 > aLanguages; + 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()) { + uno::Sequence<lang::Locale> aLocaleSequence(xSuppLoc->getLocales()); + aLanguages = LocaleSeqToLangSeq( aLocaleSequence ); + } + + pAvailGrammarSvcs->push_back( new SvcInfo( aImplName, aLanguages ) ); + } + } + } + } + } +} + + +void LngSvcMgr::GetAvailableHyphSvcs_Impl() +{ + if (!pAvailHyphSvcs) + { + pAvailHyphSvcs = new SvcInfoArray; + uno::Reference< lang::XMultiServiceFactory > xFac( utl::getProcessServiceFactory() ); + if (xFac.is()) + { + uno::Reference< container::XContentEnumerationAccess > xEnumAccess( xFac, uno::UNO_QUERY ); + uno::Reference< container::XEnumeration > xEnum; + if (xEnumAccess.is()) + xEnum = xEnumAccess->createContentEnumeration( A2OU( SN_HYPHENATOR ) ); + + if (xEnum.is()) + { + while (xEnum->hasMoreElements()) + { + uno::Any aCurrent = xEnum->nextElement(); + uno::Reference< lang::XSingleComponentFactory > xCompFactory; + uno::Reference< lang::XSingleServiceFactory > xFactory; + + uno::Reference< linguistic2::XHyphenator > xSvc; + if ( cppu::extractInterface( xCompFactory, aCurrent ) || ::cppu::extractInterface( xFactory, aCurrent ) ) + { + try + { + uno::Reference < uno::XComponentContext > xContext; + uno::Reference< beans::XPropertySet > xProps( xFac, uno::UNO_QUERY ); + + xProps->getPropertyValue( OUString( RTL_CONSTASCII_USTRINGPARAM( "DefaultContext" ))) >>= xContext; + xSvc = uno::Reference< linguistic2::XHyphenator >( ( xCompFactory.is() ? xCompFactory->createInstanceWithContext( xContext ) : xFactory->createInstance() ), uno::UNO_QUERY ); + + } + catch (uno::Exception &rEx) + { + (void) rEx; + DBG_ASSERT( 0, "createInstance failed" ); + } + } + + if (xSvc.is()) + { + OUString aImplName; + uno::Sequence< sal_Int16 > aLanguages; + 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()) { + uno::Sequence<lang::Locale> aLocaleSequence(xSuppLoc->getLocales()); + aLanguages = LocaleSeqToLangSeq( aLocaleSequence ); + } + + pAvailHyphSvcs->push_back( new SvcInfo( aImplName, aLanguages ) ); + } + } + } + } + } +} + + +void LngSvcMgr::GetAvailableThesSvcs_Impl() +{ + if (!pAvailThesSvcs) + { + pAvailThesSvcs = new SvcInfoArray; + + uno::Reference< lang::XMultiServiceFactory > xFac( utl::getProcessServiceFactory() ); + if (xFac.is()) + { + uno::Reference< container::XContentEnumerationAccess > xEnumAccess( xFac, uno::UNO_QUERY ); + uno::Reference< container::XEnumeration > xEnum; + if (xEnumAccess.is()) + xEnum = xEnumAccess->createContentEnumeration( + A2OU( SN_THESAURUS ) ); + + if (xEnum.is()) + { + while (xEnum->hasMoreElements()) + { + uno::Any aCurrent = xEnum->nextElement(); + + uno::Reference< lang::XSingleComponentFactory > xCompFactory; + uno::Reference< lang::XSingleServiceFactory > xFactory; + + uno::Reference< linguistic2::XThesaurus > xSvc; + if ( cppu::extractInterface( xCompFactory, aCurrent ) || ::cppu::extractInterface( xFactory, aCurrent ) ) + { + try + { + uno::Reference < uno::XComponentContext > xContext; + uno::Reference< beans::XPropertySet > xProps( xFac, uno::UNO_QUERY ); + + xProps->getPropertyValue( OUString( RTL_CONSTASCII_USTRINGPARAM( "DefaultContext" ))) >>= xContext; + xSvc = uno::Reference< linguistic2::XThesaurus >( ( xCompFactory.is() ? xCompFactory->createInstanceWithContext( xContext ) : xFactory->createInstance() ), uno::UNO_QUERY ); + } + catch (uno::Exception &rEx) + { + (void) rEx; + DBG_ASSERT( 0, "createInstance failed" ); + } + } + + if (xSvc.is()) + { + OUString aImplName; + uno::Sequence< sal_Int16 > aLanguages; + 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()) { + uno::Sequence<lang::Locale> aLocaleSequence(xSuppLoc->getLocales()); + aLanguages = LocaleSeqToLangSeq( aLocaleSequence ); + } + + pAvailThesSvcs->push_back( new SvcInfo( aImplName, aLanguages ) ); + } + } + } + } + } +} + + +void LngSvcMgr::SetCfgServiceLists( SpellCheckerDispatcher &rSpellDsp ) +{ + RTL_LOGFILE_CONTEXT( aLog, "linguistic: LngSvcMgr::SetCfgServiceLists - Spell" ); + + String aNode( String::CreateFromAscii( "ServiceManager/SpellCheckerList" ) ); + uno::Sequence< OUString > aNames( /*aCfg.*/GetNodeNames( aNode ) ); + OUString *pNames = aNames.getArray(); + sal_Int32 nLen = aNames.getLength(); + + // append path prefix need for 'GetProperties' call below + String aPrefix( aNode ); + aPrefix.Append( (sal_Unicode) '/' ); + for (int i = 0; i < nLen; ++i) + { + OUString aTmp( aPrefix ); + aTmp += pNames[i]; + pNames[i] = aTmp; + } + + uno::Sequence< uno::Any > aValues( /*aCfg.*/GetProperties( aNames ) ); + if (nLen && nLen == aValues.getLength()) + { + const uno::Any *pValues = aValues.getConstArray(); + for (sal_Int32 i = 0; i < nLen; ++i) + { + uno::Sequence< OUString > aSvcImplNames; + if (pValues[i] >>= aSvcImplNames) + { + String aLocaleStr( pNames[i] ); + xub_StrLen nSeperatorPos = aLocaleStr.SearchBackward( sal_Unicode( '/' ) ); + aLocaleStr = aLocaleStr.Copy( nSeperatorPos + 1 ); + lang::Locale aLocale( CreateLocale( MsLangId::convertIsoStringToLanguage(aLocaleStr) ) ); + rSpellDsp.SetServiceList( aLocale, aSvcImplNames ); + } + } + } +} + + +void LngSvcMgr::SetCfgServiceLists( GrammarCheckingIterator &rGrammarDsp ) +{ + RTL_LOGFILE_CONTEXT( aLog, "linguistic: LngSvcMgr::SetCfgServiceLists - Grammar" ); + + String aNode( String::CreateFromAscii( "ServiceManager/GrammarCheckerList" ) ); + uno::Sequence< OUString > aNames( /*aCfg.*/GetNodeNames( aNode ) ); + OUString *pNames = aNames.getArray(); + sal_Int32 nLen = aNames.getLength(); + + // append path prefix need for 'GetProperties' call below + String aPrefix( aNode ); + aPrefix.Append( (sal_Unicode) '/' ); + for (int i = 0; i < nLen; ++i) + { + OUString aTmp( aPrefix ); + aTmp += pNames[i]; + pNames[i] = aTmp; + } + + uno::Sequence< uno::Any > aValues( /*aCfg.*/GetProperties( aNames ) ); + if (nLen && nLen == aValues.getLength()) + { + const uno::Any *pValues = aValues.getConstArray(); + for (sal_Int32 i = 0; i < nLen; ++i) + { + uno::Sequence< OUString > aSvcImplNames; + if (pValues[i] >>= aSvcImplNames) + { + // there should only be one grammar checker in use per language... + if (aSvcImplNames.getLength() > 1) + aSvcImplNames.realloc(1); + + String aLocaleStr( pNames[i] ); + xub_StrLen nSeperatorPos = aLocaleStr.SearchBackward( sal_Unicode( '/' ) ); + aLocaleStr = aLocaleStr.Copy( nSeperatorPos + 1 ); + lang::Locale aLocale( CreateLocale( MsLangId::convertIsoStringToLanguage(aLocaleStr) ) ); + rGrammarDsp.SetServiceList( aLocale, aSvcImplNames ); + } + } + } +} + + +void LngSvcMgr::SetCfgServiceLists( HyphenatorDispatcher &rHyphDsp ) +{ + RTL_LOGFILE_CONTEXT( aLog, "linguistic: LngSvcMgr::SetCfgServiceLists - Hyph" ); + + String aNode( String::CreateFromAscii( "ServiceManager/HyphenatorList" ) ); + uno::Sequence< OUString > aNames( /*aCfg.*/GetNodeNames( aNode ) ); + OUString *pNames = aNames.getArray(); + sal_Int32 nLen = aNames.getLength(); + + // append path prefix need for 'GetProperties' call below + String aPrefix( aNode ); + aPrefix.Append( (sal_Unicode) '/' ); + for (int i = 0; i < nLen; ++i) + { + OUString aTmp( aPrefix ); + aTmp += pNames[i]; + pNames[i] = aTmp; + } + + uno::Sequence< uno::Any > aValues( /*aCfg.*/GetProperties( aNames ) ); + if (nLen && nLen == aValues.getLength()) + { + const uno::Any *pValues = aValues.getConstArray(); + for (sal_Int32 i = 0; i < nLen; ++i) + { + uno::Sequence< OUString > aSvcImplNames; + if (pValues[i] >>= aSvcImplNames) + { + // there should only be one hyphenator in use per language... + if (aSvcImplNames.getLength() > 1) + aSvcImplNames.realloc(1); + + String aLocaleStr( pNames[i] ); + xub_StrLen nSeperatorPos = aLocaleStr.SearchBackward( sal_Unicode( '/' ) ); + aLocaleStr = aLocaleStr.Copy( nSeperatorPos + 1 ); + lang::Locale aLocale( CreateLocale( MsLangId::convertIsoStringToLanguage(aLocaleStr) ) ); + rHyphDsp.SetServiceList( aLocale, aSvcImplNames ); + } + } + } +} + + +void LngSvcMgr::SetCfgServiceLists( ThesaurusDispatcher &rThesDsp ) +{ + RTL_LOGFILE_CONTEXT( aLog, "linguistic: LngSvcMgr::SetCfgServiceLists - Thes" ); + + String aNode( String::CreateFromAscii( "ServiceManager/ThesaurusList" ) ); + uno::Sequence< OUString > aNames( /*aCfg.*/GetNodeNames( aNode ) ); + OUString *pNames = aNames.getArray(); + sal_Int32 nLen = aNames.getLength(); + + // append path prefix need for 'GetProperties' call below + String aPrefix( aNode ); + aPrefix.Append( (sal_Unicode) '/' ); + for (int i = 0; i < nLen; ++i) + { + OUString aTmp( aPrefix ); + aTmp += pNames[i]; + pNames[i] = aTmp; + } + + uno::Sequence< uno::Any > aValues( /*aCfg.*/GetProperties( aNames ) ); + if (nLen && nLen == aValues.getLength()) + { + const uno::Any *pValues = aValues.getConstArray(); + for (sal_Int32 i = 0; i < nLen; ++i) + { + uno::Sequence< OUString > aSvcImplNames; + if (pValues[i] >>= aSvcImplNames) + { + String aLocaleStr( pNames[i] ); + xub_StrLen nSeperatorPos = aLocaleStr.SearchBackward( sal_Unicode( '/' ) ); + aLocaleStr = aLocaleStr.Copy( nSeperatorPos + 1 ); + lang::Locale aLocale( CreateLocale( MsLangId::convertIsoStringToLanguage(aLocaleStr) ) ); + rThesDsp.SetServiceList( aLocale, aSvcImplNames ); + } + } + } +} + + +uno::Reference< linguistic2::XSpellChecker > SAL_CALL + LngSvcMgr::getSpellChecker() + throw(uno::RuntimeException) +{ + osl::MutexGuard aGuard( GetLinguMutex() ); +#if OSL_DEBUG_LEVEL > 1 + getAvailableLocales( A2OU( SN_SPELLCHECKER )); +#endif + + uno::Reference< linguistic2::XSpellChecker > xRes; + if (!bDisposing) + { + if (!xSpellDsp.is()) + GetSpellCheckerDsp_Impl(); + xRes = xSpellDsp; + } + return xRes; +} + + +uno::Reference< linguistic2::XHyphenator > SAL_CALL + LngSvcMgr::getHyphenator() + throw(uno::RuntimeException) +{ + osl::MutexGuard aGuard( GetLinguMutex() ); +#if OSL_DEBUG_LEVEL > 1 + getAvailableLocales( A2OU( SN_HYPHENATOR )); +#endif + + uno::Reference< linguistic2::XHyphenator > xRes; + if (!bDisposing) + { + if (!xHyphDsp.is()) + GetHyphenatorDsp_Impl(); + xRes = xHyphDsp; + } + return xRes; +} + + +uno::Reference< linguistic2::XThesaurus > SAL_CALL + LngSvcMgr::getThesaurus() + throw(uno::RuntimeException) +{ + osl::MutexGuard aGuard( GetLinguMutex() ); +#if OSL_DEBUG_LEVEL > 1 + getAvailableLocales( A2OU( SN_THESAURUS )); +#endif + + uno::Reference< linguistic2::XThesaurus > xRes; + if (!bDisposing) + { + if (!xThesDsp.is()) + GetThesaurusDsp_Impl(); + xRes = xThesDsp; + } + return xRes; +} + + +sal_Bool SAL_CALL + LngSvcMgr::addLinguServiceManagerListener( + const uno::Reference< lang::XEventListener >& xListener ) + throw(uno::RuntimeException) +{ + osl::MutexGuard aGuard( GetLinguMutex() ); + + sal_Bool bRes = sal_False; + if (!bDisposing && xListener.is()) + { + if (!pListenerHelper) + GetListenerHelper_Impl(); + bRes = pListenerHelper->AddLngSvcMgrListener( xListener ); + } + return bRes; +} + + +sal_Bool SAL_CALL + LngSvcMgr::removeLinguServiceManagerListener( + const uno::Reference< lang::XEventListener >& xListener ) + throw(uno::RuntimeException) +{ + osl::MutexGuard aGuard( GetLinguMutex() ); + + sal_Bool bRes = sal_False; + if (!bDisposing && xListener.is()) + { + DBG_ASSERT( pListenerHelper, "listener removed without being added" ); + if (!pListenerHelper) + GetListenerHelper_Impl(); + bRes = pListenerHelper->RemoveLngSvcMgrListener( xListener ); + } + return bRes; +} + + +uno::Sequence< OUString > SAL_CALL + LngSvcMgr::getAvailableServices( + const OUString& rServiceName, + const lang::Locale& rLocale ) + throw(uno::RuntimeException) +{ + osl::MutexGuard aGuard( GetLinguMutex() ); + + uno::Sequence< OUString > aRes; + const SvcInfoArray *pInfoArray = 0; + + if (0 == rServiceName.compareToAscii( SN_SPELLCHECKER )) + { + // don't used cached data here (force re-evaluation in order to have downloaded dictionaries + // already found without the need to restart the office + delete pAvailSpellSvcs; pAvailSpellSvcs = 0; + GetAvailableSpellSvcs_Impl(); + pInfoArray = pAvailSpellSvcs; + } + else if (0 == rServiceName.compareToAscii( SN_GRAMMARCHECKER )) + { + // don't used cached data here (force re-evaluation in order to have downloaded dictionaries + // already found without the need to restart the office + delete pAvailGrammarSvcs; pAvailGrammarSvcs = 0; + GetAvailableGrammarSvcs_Impl(); + pInfoArray = pAvailGrammarSvcs; + } + else if (0 == rServiceName.compareToAscii( SN_HYPHENATOR )) + { + // don't used cached data here (force re-evaluation in order to have downloaded dictionaries + // already found without the need to restart the office + delete pAvailHyphSvcs; pAvailHyphSvcs = 0; + GetAvailableHyphSvcs_Impl(); + pInfoArray = pAvailHyphSvcs; + } + else if (0 == rServiceName.compareToAscii( SN_THESAURUS )) + { + // don't used cached data here (force re-evaluation in order to have downloaded dictionaries + // already found without the need to restart the office + delete pAvailThesSvcs; pAvailThesSvcs = 0; + GetAvailableThesSvcs_Impl(); + pInfoArray = pAvailThesSvcs; + } + + if (pInfoArray) + { + // resize to max number of entries + size_t nMaxCnt = pInfoArray->size(); + aRes.realloc( nMaxCnt ); + OUString *pImplName = aRes.getArray(); + + sal_uInt16 nCnt = 0; + LanguageType nLanguage = LocaleToLanguage( rLocale ); + for (size_t i = 0; i < nMaxCnt; ++i) + { + const SvcInfo &rInfo = (*pInfoArray)[i]; + if (LANGUAGE_NONE == nLanguage + || rInfo.HasLanguage( nLanguage )) + { + pImplName[ nCnt++ ] = rInfo.aSvcImplName; + } + } + + // resize to actual number of entries + if (nCnt != nMaxCnt) + aRes.realloc( nCnt ); + } + + return aRes; +} + + +uno::Sequence< lang::Locale > SAL_CALL + LngSvcMgr::getAvailableLocales( + const OUString& rServiceName ) + throw(uno::RuntimeException) +{ + osl::MutexGuard aGuard( GetLinguMutex() ); + + uno::Sequence< lang::Locale > aRes; + + uno::Sequence< lang::Locale > *pAvailLocales = NULL; + if (0 == rServiceName.compareToAscii( SN_SPELLCHECKER )) + pAvailLocales = &aAvailSpellLocales; + else if (0 == rServiceName.compareToAscii( SN_GRAMMARCHECKER )) + pAvailLocales = &aAvailGrammarLocales; + else if (0 == rServiceName.compareToAscii( SN_HYPHENATOR )) + pAvailLocales = &aAvailHyphLocales; + else if (0 == rServiceName.compareToAscii( SN_THESAURUS )) + pAvailLocales = &aAvailThesLocales; + + // Nowadays (with OOo lingu in SO) we want to know immediately about + // new downloaded dictionaries and have them ready right away if the Tools/Options... + // is used to activate them. Thus we can not rely anymore on buffered data. + if (pAvailLocales) + { + *pAvailLocales = GetAvailLocales(getAvailableServices(rServiceName, lang::Locale())); + aRes = *pAvailLocales; + } + + return aRes; +} + +static sal_Bool IsEqSvcList( const uno::Sequence< OUString > &rList1, + const uno::Sequence< OUString > &rList2 ) +{ + // returns sal_True iff both sequences are equal + + sal_Bool bRes = sal_False; + sal_Int32 nLen = rList1.getLength(); + if (rList2.getLength() == nLen) + { + const OUString *pStr1 = rList1.getConstArray(); + const OUString *pStr2 = rList2.getConstArray(); + bRes = sal_True; + for (sal_Int32 i = 0; i < nLen && bRes; ++i) + { + if (*pStr1++ != *pStr2++) + bRes = sal_False; + } + } + return bRes; +} + + +void SAL_CALL + LngSvcMgr::setConfiguredServices( + const OUString& rServiceName, + const lang::Locale& rLocale, + const uno::Sequence< OUString >& rServiceImplNames ) + throw(uno::RuntimeException) +{ + RTL_LOGFILE_CONTEXT( aLog, "linguistic: LngSvcMgr::setConfiguredServices" ); + + osl::MutexGuard aGuard( GetLinguMutex() ); + +#if OSL_DEBUG_LEVEL > 1 +#endif + + LanguageType nLanguage = LocaleToLanguage( rLocale ); + if (LANGUAGE_NONE != nLanguage) + { + if (0 == rServiceName.compareToAscii( SN_SPELLCHECKER )) + { + if (!xSpellDsp.is()) + GetSpellCheckerDsp_Impl(); + sal_Bool bChanged = !IsEqSvcList( rServiceImplNames, + pSpellDsp->GetServiceList( rLocale ) ); + if (bChanged) + { + pSpellDsp->SetServiceList( rLocale, rServiceImplNames ); + SaveCfgSvcs( A2OU( SN_SPELLCHECKER ) ); + + if (pListenerHelper && bChanged) + pListenerHelper->AddLngSvcEvt( + linguistic2::LinguServiceEventFlags::SPELL_CORRECT_WORDS_AGAIN | + linguistic2::LinguServiceEventFlags::SPELL_WRONG_WORDS_AGAIN ); + } + } + else if (0 == rServiceName.compareToAscii( SN_GRAMMARCHECKER )) + { + if (!xGrammarDsp.is()) + GetGrammarCheckerDsp_Impl(); + sal_Bool bChanged = !IsEqSvcList( rServiceImplNames, + pGrammarDsp->GetServiceList( rLocale ) ); + if (bChanged) + { + pGrammarDsp->SetServiceList( rLocale, rServiceImplNames ); + SaveCfgSvcs( A2OU( SN_GRAMMARCHECKER ) ); + + if (pListenerHelper && bChanged) + pListenerHelper->AddLngSvcEvt( + linguistic2::LinguServiceEventFlags::PROOFREAD_AGAIN ); + } + } + else if (0 == rServiceName.compareToAscii( SN_HYPHENATOR )) + { + if (!xHyphDsp.is()) + GetHyphenatorDsp_Impl(); + sal_Bool bChanged = !IsEqSvcList( rServiceImplNames, + pHyphDsp->GetServiceList( rLocale ) ); + if (bChanged) + { + pHyphDsp->SetServiceList( rLocale, rServiceImplNames ); + SaveCfgSvcs( A2OU( SN_HYPHENATOR ) ); + + if (pListenerHelper && bChanged) + pListenerHelper->AddLngSvcEvt( + linguistic2::LinguServiceEventFlags::HYPHENATE_AGAIN ); + } + } + else if (0 == rServiceName.compareToAscii( SN_THESAURUS )) + { + if (!xThesDsp.is()) + GetThesaurusDsp_Impl(); + sal_Bool bChanged = !IsEqSvcList( rServiceImplNames, + pThesDsp->GetServiceList( rLocale ) ); + if (bChanged) + { + pThesDsp->SetServiceList( rLocale, rServiceImplNames ); + SaveCfgSvcs( A2OU( SN_THESAURUS ) ); + } + } + } +} + + +sal_Bool LngSvcMgr::SaveCfgSvcs( const String &rServiceName ) +{ + RTL_LOGFILE_CONTEXT( aLog, "linguistic: LngSvcMgr::SaveCfgSvcs" ); + + sal_Bool bRes = sal_False; + + LinguDispatcher *pDsp = 0; + uno::Sequence< lang::Locale > aLocales; + + if (0 == rServiceName.CompareToAscii( SN_SPELLCHECKER )) + { + if (!pSpellDsp) + GetSpellCheckerDsp_Impl(); + pDsp = pSpellDsp; + aLocales = getAvailableLocales( A2OU( SN_SPELLCHECKER ) ); + } + else if (0 == rServiceName.CompareToAscii( SN_GRAMMARCHECKER )) + { + if (!pGrammarDsp) + GetGrammarCheckerDsp_Impl(); + pDsp = pGrammarDsp; + aLocales = getAvailableLocales( A2OU( SN_GRAMMARCHECKER ) ); + } + else if (0 == rServiceName.CompareToAscii( SN_HYPHENATOR )) + { + if (!pHyphDsp) + GetHyphenatorDsp_Impl(); + pDsp = pHyphDsp; + aLocales = getAvailableLocales( A2OU( SN_HYPHENATOR ) ); + } + else if (0 == rServiceName.CompareToAscii( SN_THESAURUS )) + { + if (!pThesDsp) + GetThesaurusDsp_Impl(); + pDsp = pThesDsp; + aLocales = getAvailableLocales( A2OU( SN_THESAURUS ) ); + } + + if (pDsp && aLocales.getLength()) + { + sal_Int32 nLen = aLocales.getLength(); + const lang::Locale *pLocale = aLocales.getConstArray(); + + uno::Sequence< beans::PropertyValue > aValues( nLen ); + beans::PropertyValue *pValues = aValues.getArray(); + beans::PropertyValue *pValue = pValues; + + // get node name to be used + const char *pNodeName = NULL; + if (pDsp == pSpellDsp) + pNodeName = "ServiceManager/SpellCheckerList"; + else if (pDsp == pGrammarDsp) + pNodeName = "ServiceManager/GrammarCheckerList"; + else if (pDsp == pHyphDsp) + pNodeName = "ServiceManager/HyphenatorList"; + else if (pDsp == pThesDsp) + pNodeName = "ServiceManager/ThesaurusList"; + else + { + DBG_ASSERT( 0, "node name missing" ); + } + OUString aNodeName( ::rtl::OUString::createFromAscii(pNodeName) ); + + for (sal_Int32 i = 0; i < nLen; ++i) + { + uno::Sequence< OUString > aSvcImplNames; + aSvcImplNames = pDsp->GetServiceList( pLocale[i] ); + +#if OSL_DEBUG_LEVEL > 1 + sal_Int32 nSvcs = aSvcImplNames.getLength(); + const OUString *pSvcImplName = aSvcImplNames.getConstArray(); + for (sal_Int32 j = 0; j < nSvcs; ++j) + { + OUString aImplName( pSvcImplName[j] ); + } +#endif + // build value to be written back to configuration + uno::Any aCfgAny; + if ((pDsp == pHyphDsp || pDsp == pGrammarDsp) && aSvcImplNames.getLength() > 1) + aSvcImplNames.realloc(1); // there should be only one entry for hyphenators or grammar checkers (because they are not chained) + aCfgAny <<= aSvcImplNames; + DBG_ASSERT( aCfgAny.hasValue(), "missing value for 'Any' type" ); + + OUString aCfgLocaleStr( MsLangId::convertLanguageToIsoString( + LocaleToLanguage( pLocale[i] ) ) ); + pValue->Value = aCfgAny; + pValue->Name = aNodeName; + pValue->Name += OUString::valueOf( (sal_Unicode) '/' ); + pValue->Name += aCfgLocaleStr; + pValue++; + } + { + RTL_LOGFILE_CONTEXT( aLog, "linguistic: LngSvcMgr::SaveCfgSvcs - ReplaceSetProperties" ); + // change, add new or replace existing entries. + bRes |= /*aCfg.*/ReplaceSetProperties( aNodeName, aValues ); + } + } + + return bRes; +} + + +static uno::Sequence< OUString > GetLangSvcList( const uno::Any &rVal ) +{ + uno::Sequence< OUString > aRes; + + if (rVal.hasValue()) + { + rVal >>= aRes; +#if OSL_DEBUG_LEVEL > 1 + sal_Int32 nSvcs = aRes.getLength(); + if (nSvcs) + { + const OUString *pSvcName = aRes.getConstArray(); + for (sal_Int32 j = 0; j < nSvcs; ++j) + { + OUString aImplName( pSvcName[j] ); + DBG_ASSERT( aImplName.getLength(), "service impl-name missing" ); + } + } +#endif + } + + return aRes; +} + + +static uno::Sequence< OUString > GetLangSvc( const uno::Any &rVal ) +{ + uno::Sequence< OUString > aRes; + if (!rVal.hasValue()) + return aRes; + + // allowing for a sequence here as well (even though it should only + // be a string) makes coding easier in other places since one needs + // not make a special case for writing a string only and not a + // sequence of strings. + if (rVal >>= aRes) + { + // but only the first string should be used. + if (aRes.getLength() > 1) + aRes.realloc(1); + } + else + { + OUString aImplName; + if ((rVal >>= aImplName) && aImplName.getLength() != 0) + { + aRes.realloc(1); + aRes.getArray()[0] = aImplName; + } + else + { + DBG_ASSERT( 0, "GetLangSvc: unexpected type encountered" ); + } + } + + return aRes; +} + + + +uno::Sequence< OUString > SAL_CALL + LngSvcMgr::getConfiguredServices( + const OUString& rServiceName, + const lang::Locale& rLocale ) + throw(uno::RuntimeException) +{ + osl::MutexGuard aGuard( GetLinguMutex() ); + + uno::Sequence< OUString > aSvcImplNames; + + LanguageType nLanguage = LocaleToLanguage( rLocale ); + OUString aCfgLocale( MsLangId::convertLanguageToIsoString( nLanguage ) ); + + uno::Sequence< uno::Any > aValues; + uno::Sequence< OUString > aNames( 1 ); + OUString *pNames = aNames.getArray(); + if ( 0 == rServiceName.compareToAscii( SN_SPELLCHECKER ) ) + { + OUString aNode( RTL_CONSTASCII_USTRINGPARAM("ServiceManager/SpellCheckerList")); + const uno::Sequence< OUString > aNodeEntries( GetNodeNames( aNode ) ); + if (lcl_SeqHasString( aNodeEntries, aCfgLocale )) + { + OUString aPropName( aNode ); + aPropName += OUString::valueOf( (sal_Unicode) '/' ); + aPropName += aCfgLocale; + pNames[0] = aPropName; + aValues = /*aCfg.*/GetProperties( aNames ); + if (aValues.getLength()) + aSvcImplNames = GetLangSvcList( aValues.getConstArray()[0] ); + } + } + else if ( 0 == rServiceName.compareToAscii( SN_GRAMMARCHECKER ) ) + { + OUString aNode( RTL_CONSTASCII_USTRINGPARAM("ServiceManager/GrammarCheckerList")); + const uno::Sequence< OUString > aNodeEntries( GetNodeNames( aNode ) ); + if (lcl_SeqHasString( aNodeEntries, aCfgLocale )) + { + OUString aPropName( aNode ); + aPropName += OUString::valueOf( (sal_Unicode) '/' ); + aPropName += aCfgLocale; + pNames[0] = aPropName; + aValues = /*aCfg.*/GetProperties( aNames ); + if (aValues.getLength()) + aSvcImplNames = GetLangSvc( aValues.getConstArray()[0] ); + } + } + else if ( 0 == rServiceName.compareToAscii( SN_HYPHENATOR ) ) + { + OUString aNode( RTL_CONSTASCII_USTRINGPARAM("ServiceManager/HyphenatorList")); + const uno::Sequence< OUString > aNodeEntries( GetNodeNames( aNode ) ); + if (lcl_SeqHasString( aNodeEntries, aCfgLocale )) + { + OUString aPropName( aNode ); + aPropName += OUString::valueOf( (sal_Unicode) '/' ); + aPropName += aCfgLocale; + pNames[0] = aPropName; + aValues = /*aCfg.*/GetProperties( aNames ); + if (aValues.getLength()) + aSvcImplNames = GetLangSvc( aValues.getConstArray()[0] ); + } + } + else if ( 0 == rServiceName.compareToAscii( SN_THESAURUS ) ) + { + OUString aNode( RTL_CONSTASCII_USTRINGPARAM("ServiceManager/ThesaurusList")); + const uno::Sequence< OUString > aNodeEntries( GetNodeNames( aNode ) ); + if (lcl_SeqHasString( aNodeEntries, aCfgLocale )) + { + OUString aPropName( aNode ); + aPropName += OUString::valueOf( (sal_Unicode) '/' ); + aPropName += aCfgLocale; + pNames[0] = aPropName; + aValues = /*aCfg.*/GetProperties( aNames ); + if (aValues.getLength()) + aSvcImplNames = GetLangSvcList( aValues.getConstArray()[0] ); + } + } + +#if OSL_DEBUG_LEVEL > 1 + const OUString *pImplNames = aSvcImplNames.getConstArray(); + (void) pImplNames; +#endif + return aSvcImplNames; +} + + +void SAL_CALL + LngSvcMgr::dispose() + throw(uno::RuntimeException) +{ + osl::MutexGuard aGuard( GetLinguMutex() ); + + if (!bDisposing) + { + bDisposing = sal_True; + + // require listeners to release this object + lang::EventObject aEvtObj( static_cast<XLinguServiceManager*>(this) ); + aEvtListeners.disposeAndClear( aEvtObj ); + + if (pListenerHelper) + pListenerHelper->DisposeAndClear( aEvtObj ); + } +} + + +void SAL_CALL + LngSvcMgr::addEventListener( + const uno::Reference< lang::XEventListener >& xListener ) + throw(uno::RuntimeException) +{ + osl::MutexGuard aGuard( GetLinguMutex() ); + + if (!bDisposing && xListener.is()) + { + aEvtListeners.addInterface( xListener ); + } +} + + +void SAL_CALL + LngSvcMgr::removeEventListener( + const uno::Reference< lang::XEventListener >& xListener ) + throw(uno::RuntimeException) +{ + osl::MutexGuard aGuard( GetLinguMutex() ); + + if (xListener.is()) + { + aEvtListeners.removeInterface( xListener ); + } +} + + +sal_Bool LngSvcMgr::AddLngSvcEvtBroadcaster( + const uno::Reference< linguistic2::XLinguServiceEventBroadcaster > &rxBroadcaster ) +{ + sal_Bool bRes = sal_False; + if (rxBroadcaster.is()) + { + if (!pListenerHelper) + GetListenerHelper_Impl(); + bRes = pListenerHelper->AddLngSvcEvtBroadcaster( rxBroadcaster ); + } + return bRes; +} + + +sal_Bool LngSvcMgr::RemoveLngSvcEvtBroadcaster( + const uno::Reference< linguistic2::XLinguServiceEventBroadcaster > &rxBroadcaster ) +{ + sal_Bool bRes = sal_False; + if (rxBroadcaster.is()) + { + DBG_ASSERT( pListenerHelper, "pListenerHelper non existent" ); + if (!pListenerHelper) + GetListenerHelper_Impl(); + bRes = pListenerHelper->RemoveLngSvcEvtBroadcaster( rxBroadcaster ); + } + return bRes; +} + + +OUString SAL_CALL + LngSvcMgr::getImplementationName() + throw(uno::RuntimeException) +{ + osl::MutexGuard aGuard( GetLinguMutex() ); + return getImplementationName_Static(); +} + + +sal_Bool SAL_CALL + LngSvcMgr::supportsService( const OUString& ServiceName ) + throw(uno::RuntimeException) +{ + osl::MutexGuard aGuard( GetLinguMutex() ); + + uno::Sequence< OUString > aSNL = getSupportedServiceNames(); + const OUString * pArray = aSNL.getConstArray(); + for( sal_Int32 i = 0; i < aSNL.getLength(); i++ ) + if( pArray[i] == ServiceName ) + return sal_True; + return sal_False; +} + + +uno::Sequence< OUString > SAL_CALL + LngSvcMgr::getSupportedServiceNames() + throw(uno::RuntimeException) +{ + osl::MutexGuard aGuard( GetLinguMutex() ); + return getSupportedServiceNames_Static(); +} + + +uno::Sequence< OUString > LngSvcMgr::getSupportedServiceNames_Static() + throw() +{ + osl::MutexGuard aGuard( GetLinguMutex() ); + + uno::Sequence< OUString > aSNS( 1 ); // more than 1 service possible + aSNS.getArray()[0] = A2OU( SN_LINGU_SERVCICE_MANAGER ); + return aSNS; +} + + +uno::Reference< uno::XInterface > SAL_CALL LngSvcMgr_CreateInstance( + const uno::Reference< lang::XMultiServiceFactory > & /*rSMgr*/ ) + throw(uno::Exception) +{ + uno::Reference< uno::XInterface > xService = (cppu::OWeakObject*) new LngSvcMgr; + return xService; +} + +void * SAL_CALL LngSvcMgr_getFactory( + const sal_Char * pImplName, + lang::XMultiServiceFactory * pServiceManager, + void * /*pRegistryKey*/ ) +{ + + void * pRet = 0; + if ( !LngSvcMgr::getImplementationName_Static().compareToAscii( pImplName ) ) + { + uno::Reference< lang::XSingleServiceFactory > xFactory = + cppu::createOneInstanceFactory( + pServiceManager, + LngSvcMgr::getImplementationName_Static(), + LngSvcMgr_CreateInstance, + LngSvcMgr::getSupportedServiceNames_Static()); + // acquire, because we return an interface pointer instead of a reference + xFactory->acquire(); + pRet = xFactory.get(); + } + return pRet; +} + + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/linguistic/source/lngsvcmgr.hxx b/linguistic/source/lngsvcmgr.hxx new file mode 100644 index 000000000000..635a85318364 --- /dev/null +++ b/linguistic/source/lngsvcmgr.hxx @@ -0,0 +1,192 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _LINGUISTIC_LNGSVCMGR_HXX_ +#define _LINGUISTIC_LNGSVCMGR_HXX_ + +#include <uno/lbnames.h> // CPPU_CURRENT_LANGUAGE_BINDING_NAME macro, which specify the environment type +#include <cppuhelper/implbase4.hxx> // helper for implementations +#include <cppuhelper/interfacecontainer.h> //OMultiTypeInterfaceContainerHelper + + +#include <com/sun/star/uno/Reference.h> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/XComponent.hpp> +#include <com/sun/star/linguistic2/XLinguServiceManager.hpp> +#include <com/sun/star/linguistic2/XAvailableLocales.hpp> +#include <unotools/configitem.hxx> + +#include <boost/ptr_container/ptr_vector.hpp> + +#include "linguistic/misc.hxx" +#include "defs.hxx" + +class SpellCheckerDispatcher; +class HyphenatorDispatcher; +class ThesaurusDispatcher; +class GrammarCheckingIterator; +class LngSvcMgrListenerHelper; +struct SvcInfo; + +namespace com { namespace sun { namespace star { namespace linguistic2 { + class XLinguServiceEventBroadcaster; + class XSpellChecker; + class XProofreader; + class XProofreadingIterator; + class XHyphenator; + class XThesaurus; +} } } } + + + +class LngSvcMgr : + public cppu::WeakImplHelper4 + < + com::sun::star::linguistic2::XLinguServiceManager, + com::sun::star::linguistic2::XAvailableLocales, + com::sun::star::lang::XComponent, + com::sun::star::lang::XServiceInfo + >, + private utl::ConfigItem +{ + friend class LngSvcMgrListenerHelper; + + ::cppu::OInterfaceContainerHelper aEvtListeners; + + com::sun::star::uno::Reference< + ::com::sun::star::linguistic2::XSpellChecker > xSpellDsp; + com::sun::star::uno::Reference< + ::com::sun::star::linguistic2::XProofreadingIterator > xGrammarDsp; + com::sun::star::uno::Reference< + ::com::sun::star::linguistic2::XHyphenator > xHyphDsp; + com::sun::star::uno::Reference< + ::com::sun::star::linguistic2::XThesaurus > xThesDsp; + + com::sun::star::uno::Reference< + ::com::sun::star::lang::XEventListener > xListenerHelper; + + com::sun::star::uno::Sequence< + com::sun::star::lang::Locale > aAvailSpellLocales; + com::sun::star::uno::Sequence< + com::sun::star::lang::Locale > aAvailGrammarLocales; + com::sun::star::uno::Sequence< + com::sun::star::lang::Locale > aAvailHyphLocales; + com::sun::star::uno::Sequence< + com::sun::star::lang::Locale > aAvailThesLocales; + + SpellCheckerDispatcher * pSpellDsp; + GrammarCheckingIterator * pGrammarDsp; + HyphenatorDispatcher * pHyphDsp; + ThesaurusDispatcher * pThesDsp; + + LngSvcMgrListenerHelper * pListenerHelper; + + typedef boost::ptr_vector< SvcInfo > SvcInfoArray; + SvcInfoArray * pAvailSpellSvcs; + SvcInfoArray * pAvailGrammarSvcs; + SvcInfoArray * pAvailHyphSvcs; + SvcInfoArray * pAvailThesSvcs; + + sal_Bool bDisposing; + + // disallow copy-constructor and assignment-operator for now + LngSvcMgr(const LngSvcMgr &); + LngSvcMgr & operator = (const LngSvcMgr &); + + void GetAvailableSpellSvcs_Impl(); + void GetAvailableGrammarSvcs_Impl(); + void GetAvailableHyphSvcs_Impl(); + void GetAvailableThesSvcs_Impl(); + void GetListenerHelper_Impl(); + + void GetSpellCheckerDsp_Impl( sal_Bool bSetSvcList = sal_True ); + void GetGrammarCheckerDsp_Impl( sal_Bool bSetSvcList = sal_True ); + void GetHyphenatorDsp_Impl( sal_Bool bSetSvcList = sal_True ); + void GetThesaurusDsp_Impl( sal_Bool bSetSvcList = sal_True ); + + void SetCfgServiceLists( SpellCheckerDispatcher &rSpellDsp ); + void SetCfgServiceLists( GrammarCheckingIterator &rGrammarDsp ); + void SetCfgServiceLists( HyphenatorDispatcher &rHyphDsp ); + void SetCfgServiceLists( ThesaurusDispatcher &rThesDsp ); + + sal_Bool SaveCfgSvcs( const String &rServiceName ); + + // utl::ConfigItem (to allow for listening of changes of relevant properties) + virtual void Notify( const com::sun::star::uno::Sequence< rtl::OUString > &rPropertyNames ); + virtual void Commit(); + +public: + LngSvcMgr(); + virtual ~LngSvcMgr(); + + // XLinguServiceManager + virtual ::com::sun::star::uno::Reference< ::com::sun::star::linguistic2::XSpellChecker > SAL_CALL getSpellChecker( ) throw (::com::sun::star::uno::RuntimeException); + virtual ::com::sun::star::uno::Reference< ::com::sun::star::linguistic2::XHyphenator > SAL_CALL getHyphenator( ) throw (::com::sun::star::uno::RuntimeException); + virtual ::com::sun::star::uno::Reference< ::com::sun::star::linguistic2::XThesaurus > SAL_CALL getThesaurus( ) throw (::com::sun::star::uno::RuntimeException); + virtual ::sal_Bool SAL_CALL addLinguServiceManagerListener( const ::com::sun::star::uno::Reference< ::com::sun::star::lang::XEventListener >& xListener ) throw (::com::sun::star::uno::RuntimeException); + virtual ::sal_Bool SAL_CALL removeLinguServiceManagerListener( const ::com::sun::star::uno::Reference< ::com::sun::star::lang::XEventListener >& xListener ) throw (::com::sun::star::uno::RuntimeException); + virtual ::com::sun::star::uno::Sequence< ::rtl::OUString > SAL_CALL getAvailableServices( const ::rtl::OUString& aServiceName, const ::com::sun::star::lang::Locale& aLocale ) throw (::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL setConfiguredServices( const ::rtl::OUString& aServiceName, const ::com::sun::star::lang::Locale& aLocale, const ::com::sun::star::uno::Sequence< ::rtl::OUString >& aServiceImplNames ) throw (::com::sun::star::uno::RuntimeException); + virtual ::com::sun::star::uno::Sequence< ::rtl::OUString > SAL_CALL getConfiguredServices( const ::rtl::OUString& aServiceName, const ::com::sun::star::lang::Locale& aLocale ) throw (::com::sun::star::uno::RuntimeException); + + // XAvailableLocales + virtual ::com::sun::star::uno::Sequence< ::com::sun::star::lang::Locale > SAL_CALL getAvailableLocales( const ::rtl::OUString& aServiceName ) throw (::com::sun::star::uno::RuntimeException); + + // XComponent + virtual void SAL_CALL dispose( ) throw (::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL addEventListener( const ::com::sun::star::uno::Reference< ::com::sun::star::lang::XEventListener >& xListener ) throw (::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL removeEventListener( const ::com::sun::star::uno::Reference< ::com::sun::star::lang::XEventListener >& aListener ) throw (::com::sun::star::uno::RuntimeException); + + // XServiceInfo + virtual ::rtl::OUString SAL_CALL getImplementationName( ) throw (::com::sun::star::uno::RuntimeException); + virtual ::sal_Bool SAL_CALL supportsService( const ::rtl::OUString& ServiceName ) throw (::com::sun::star::uno::RuntimeException); + virtual ::com::sun::star::uno::Sequence< ::rtl::OUString > SAL_CALL getSupportedServiceNames( ) throw (::com::sun::star::uno::RuntimeException); + + + static inline ::rtl::OUString getImplementationName_Static(); + static ::com::sun::star::uno::Sequence< ::rtl::OUString > getSupportedServiceNames_Static() throw(); + + sal_Bool AddLngSvcEvtBroadcaster( + const ::com::sun::star::uno::Reference< + ::com::sun::star::linguistic2::XLinguServiceEventBroadcaster > &rxBroadcaster ); + sal_Bool RemoveLngSvcEvtBroadcaster( + const ::com::sun::star::uno::Reference< + ::com::sun::star::linguistic2::XLinguServiceEventBroadcaster > &rxBroadcaster ); +}; + + +inline ::rtl::OUString LngSvcMgr::getImplementationName_Static() +{ + return A2OU( "com.sun.star.lingu2.LngSvcMgr" ); +} + + + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/linguistic/source/misc.cxx b/linguistic/source/misc.cxx new file mode 100644 index 000000000000..0bc393605752 --- /dev/null +++ b/linguistic/source/misc.cxx @@ -0,0 +1,911 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_linguistic.hxx" +#include <sal/macros.h> +#include <tools/string.hxx> +#include <tools/fsys.hxx> +#include <tools/debug.hxx> +#include <unotools/pathoptions.hxx> +#include <svl/lngmisc.hxx> +#include <ucbhelper/content.hxx> +#include <i18npool/mslangid.hxx> +#include <com/sun/star/ucb/XCommandEnvironment.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/beans/XFastPropertySet.hpp> +#include <com/sun/star/beans/XPropertyChangeListener.hpp> +#include <com/sun/star/frame/XTerminateListener.hpp> +#include <com/sun/star/frame/XDesktop.hpp> +#include <com/sun/star/frame/XStorable.hpp> + +#include <com/sun/star/beans/PropertyValues.hpp> +#include <com/sun/star/uno/Sequence.hxx> +#include <com/sun/star/uno/Reference.h> +#include <com/sun/star/linguistic2/DictionaryType.hpp> +#include <com/sun/star/linguistic2/XSearchableDictionaryList.hpp> +#include <unotools/processfactory.hxx> +#include <unotools/localedatawrapper.hxx> +#include <unotools/syslocale.hxx> + +#include <rtl/instance.hxx> + +#include "linguistic/misc.hxx" +#include "defs.hxx" +#include "linguistic/lngprops.hxx" +#include "linguistic/hyphdta.hxx" +#include <i18npool/mslangid.hxx> + +using namespace utl; +using namespace osl; +using namespace com::sun::star; +using namespace com::sun::star::beans; +using namespace com::sun::star::lang; +using namespace com::sun::star::uno; +using namespace com::sun::star::i18n; +using namespace com::sun::star::linguistic2; + +using ::rtl::OUString; + +namespace linguistic +{ + + +//!! multi-thread safe mutex for all platforms !! +struct LinguMutex : public rtl::Static< osl::Mutex, LinguMutex > +{ +}; + +osl::Mutex & GetLinguMutex() +{ + return LinguMutex::get(); +} + + +LocaleDataWrapper & GetLocaleDataWrapper( sal_Int16 nLang ) +{ + static LocaleDataWrapper aLclDtaWrp( + getProcessServiceFactory(), + CreateLocale( SvtSysLocale().GetUILanguage() ) ); + + const Locale &rLcl = aLclDtaWrp.getLoadedLocale(); + Locale aLcl( CreateLocale( nLang ) ); + if (aLcl.Language != rLcl.Language || + aLcl.Country != rLcl.Country || + aLcl.Variant != rLcl.Variant) + aLclDtaWrp.setLocale( aLcl ); + return aLclDtaWrp; +} + +static inline sal_Int32 Minimum( sal_Int32 n1, sal_Int32 n2, sal_Int32 n3 ) +{ + sal_Int32 nMin = n1 < n2 ? n1 : n2; + return nMin < n3 ? nMin : n3; +} + +class IntArray2D +{ +private: + sal_Int32 *pData; + int n1, n2; + +public: + IntArray2D( int nDim1, int nDim2 ); + ~IntArray2D(); + + sal_Int32 & Value( int i, int k ); +}; + +IntArray2D::IntArray2D( int nDim1, int nDim2 ) +{ + n1 = nDim1; + n2 = nDim2; + pData = new sal_Int32[n1 * n2]; +} + +IntArray2D::~IntArray2D() +{ + delete[] pData; +} + +sal_Int32 & IntArray2D::Value( int i, int k ) +{ + DBG_ASSERT( 0 <= i && i < n1, "first index out of range" ); + DBG_ASSERT( 0 <= k && k < n2, "first index out of range" ); + DBG_ASSERT( i * n2 + k < n1 * n2, "index out of range" ); + return pData[ i * n2 + k ]; +} + + +sal_Int32 LevDistance( const OUString &rTxt1, const OUString &rTxt2 ) +{ + sal_Int32 nLen1 = rTxt1.getLength(); + sal_Int32 nLen2 = rTxt2.getLength(); + + if (nLen1 == 0) + return nLen2; + if (nLen2 == 0) + return nLen1; + + IntArray2D aData( nLen1 + 1, nLen2 + 1 ); + + sal_Int32 i, k; + for (i = 0; i <= nLen1; ++i) + aData.Value(i, 0) = i; + for (k = 0; k <= nLen2; ++k) + aData.Value(0, k) = k; + for (i = 1; i <= nLen1; ++i) + { + for (k = 1; k <= nLen2; ++k) + { + sal_Unicode c1i = rTxt1.getStr()[i - 1]; + sal_Unicode c2k = rTxt2.getStr()[k - 1]; + sal_Int32 nCost = c1i == c2k ? 0 : 1; + sal_Int32 nNew = Minimum( aData.Value(i-1, k ) + 1, + aData.Value(i , k-1) + 1, + aData.Value(i-1, k-1) + nCost ); + // take transposition (exchange with left or right char) in account + if (2 < i && 2 < k) + { + int nT = aData.Value(i-2, k-2) + 1; + if (rTxt1.getStr()[i - 2] != c1i) + ++nT; + if (rTxt2.getStr()[k - 2] != c2k) + ++nT; + if (nT < nNew) + nNew = nT; + } + + aData.Value(i, k) = nNew; + } + } + sal_Int32 nDist = aData.Value(nLen1, nLen2); + return nDist; + } + + +sal_Bool IsUseDicList( const PropertyValues &rProperties, + const uno::Reference< XPropertySet > &rxProp ) +{ + sal_Bool bRes = sal_True; + + sal_Int32 nLen = rProperties.getLength(); + const PropertyValue *pVal = rProperties.getConstArray(); + sal_Int32 i; + + for ( i = 0; i < nLen; ++i) + { + if (UPH_IS_USE_DICTIONARY_LIST == pVal[i].Handle) + { + pVal[i].Value >>= bRes; + break; + } + } + if (i >= nLen) // no temporary value found in 'rProperties' + { + uno::Reference< XFastPropertySet > xFast( rxProp, UNO_QUERY ); + if (xFast.is()) + xFast->getFastPropertyValue( UPH_IS_USE_DICTIONARY_LIST ) >>= bRes; + } + + return bRes; +} + + +sal_Bool IsIgnoreControlChars( const PropertyValues &rProperties, + const uno::Reference< XPropertySet > &rxProp ) +{ + sal_Bool bRes = sal_True; + + sal_Int32 nLen = rProperties.getLength(); + const PropertyValue *pVal = rProperties.getConstArray(); + sal_Int32 i; + + for ( i = 0; i < nLen; ++i) + { + if (UPH_IS_IGNORE_CONTROL_CHARACTERS == pVal[i].Handle) + { + pVal[i].Value >>= bRes; + break; + } + } + if (i >= nLen) // no temporary value found in 'rProperties' + { + uno::Reference< XFastPropertySet > xFast( rxProp, UNO_QUERY ); + if (xFast.is()) + xFast->getFastPropertyValue( UPH_IS_IGNORE_CONTROL_CHARACTERS ) >>= bRes; + } + + return bRes; +} + + +static sal_Bool lcl_HasHyphInfo( const uno::Reference<XDictionaryEntry> &xEntry ) +{ + sal_Bool bRes = sal_False; + if (xEntry.is()) + { + // there has to be (at least one) '=' denoting a hyphenation position + // and it must not be before any character of the word + sal_Int32 nIdx = xEntry->getDictionaryWord().indexOf( '=' ); + bRes = nIdx != -1 && nIdx != 0; + } + return bRes; +} + + +uno::Reference< XDictionaryEntry > SearchDicList( + const uno::Reference< XDictionaryList > &xDicList, + const OUString &rWord, sal_Int16 nLanguage, + sal_Bool bSearchPosDics, sal_Bool bSearchSpellEntry ) +{ + MutexGuard aGuard( GetLinguMutex() ); + + uno::Reference< XDictionaryEntry > xEntry; + + if (!xDicList.is()) + return xEntry; + + const uno::Sequence< uno::Reference< XDictionary > > + aDics( xDicList->getDictionaries() ); + const uno::Reference< XDictionary > + *pDic = aDics.getConstArray(); + sal_Int32 nDics = xDicList->getCount(); + + sal_Int32 i; + for (i = 0; i < nDics; i++) + { + uno::Reference< XDictionary > axDic( pDic[i], UNO_QUERY ); + + DictionaryType eType = axDic->getDictionaryType(); + sal_Int16 nLang = LocaleToLanguage( axDic->getLocale() ); + + if ( axDic.is() && axDic->isActive() + && (nLang == nLanguage || nLang == LANGUAGE_NONE) ) + { + DBG_ASSERT( eType != DictionaryType_MIXED, + "lng : unexpected dictionary type" ); + + if ( (!bSearchPosDics && eType == DictionaryType_NEGATIVE) + || ( bSearchPosDics && eType == DictionaryType_POSITIVE)) + { + if ( (xEntry = axDic->getEntry( rWord )).is() ) + { + if (bSearchSpellEntry || lcl_HasHyphInfo( xEntry )) + break; + } + xEntry = 0; + } + } + } + + return xEntry; +} + + +sal_Bool SaveDictionaries( const uno::Reference< XDictionaryList > &xDicList ) +{ + if (!xDicList.is()) + return sal_True; + + sal_Bool bRet = sal_True; + + Sequence< uno::Reference< XDictionary > > aDics( xDicList->getDictionaries() ); + const uno::Reference< XDictionary > *pDic = aDics.getConstArray(); + sal_Int32 nCount = aDics.getLength(); + for (sal_Int32 i = 0; i < nCount; i++) + { + try + { + uno::Reference< frame::XStorable > xStor( pDic[i], UNO_QUERY ); + if (xStor.is()) + { + if (!xStor->isReadonly() && xStor->hasLocation()) + xStor->store(); + } + } + catch(uno::Exception &) + { + bRet = sal_False; + } + } + + return bRet; +} + + +sal_uInt8 AddEntryToDic( + uno::Reference< XDictionary > &rxDic, + const OUString &rWord, sal_Bool bIsNeg, + const OUString &rRplcTxt, sal_Int16 /* nRplcLang */, + sal_Bool bStripDot ) +{ + if (!rxDic.is()) + return DIC_ERR_NOT_EXISTS; + + OUString aTmp( rWord ); + if (bStripDot) + { + sal_Int32 nLen = rWord.getLength(); + if (nLen > 0 && '.' == rWord[ nLen - 1]) + { + // remove trailing '.' + // (this is the official way to do this :-( ) + aTmp = aTmp.copy( 0, nLen - 1 ); + } + } + sal_Bool bAddOk = rxDic->add( aTmp, bIsNeg, rRplcTxt ); + + sal_uInt8 nRes = DIC_ERR_NONE; + if (!bAddOk) + { + if (rxDic->isFull()) + nRes = DIC_ERR_FULL; + else + { + uno::Reference< frame::XStorable > xStor( rxDic, UNO_QUERY ); + if (xStor.is() && xStor->isReadonly()) + nRes = DIC_ERR_READONLY; + else + nRes = DIC_ERR_UNKNOWN; + } + } + + return nRes; +} + + + +LanguageType LocaleToLanguage( const Locale& rLocale ) +{ + // empty Locale -> LANGUAGE_NONE + if ( rLocale.Language.getLength() == 0 ) + return LANGUAGE_NONE; + + return MsLangId::convertLocaleToLanguage( rLocale ); +} + + +Locale& LanguageToLocale( Locale& rLocale, LanguageType eLang ) +{ + if ( eLang != LANGUAGE_NONE /* && eLang != LANGUAGE_SYSTEM */) + MsLangId::convertLanguageToLocale( eLang, rLocale ); + + return rLocale; +} + +Locale CreateLocale( LanguageType eLang ) +{ + Locale aLocale; + if ( eLang != LANGUAGE_NONE /* && eLang != LANGUAGE_SYSTEM */) + return MsLangId::convertLanguageToLocale( eLang ); + + return aLocale; +} + +uno::Sequence< sal_Int16 > + LocaleSeqToLangSeq( uno::Sequence< Locale > &rLocaleSeq ) +{ + const Locale *pLocale = rLocaleSeq.getConstArray(); + sal_Int32 nCount = rLocaleSeq.getLength(); + + uno::Sequence< sal_Int16 > aLangs( nCount ); + sal_Int16 *pLang = aLangs.getArray(); + for (sal_Int32 i = 0; i < nCount; ++i) + { + pLang[i] = LocaleToLanguage( pLocale[i] ); + } + + return aLangs; +} + + +sal_Bool IsReadOnly( const String &rURL, sal_Bool *pbExist ) +{ + sal_Bool bRes = sal_False; + sal_Bool bExists = sal_False; + + if (rURL.Len() > 0) + { + try + { + uno::Reference< ::com::sun::star::ucb::XCommandEnvironment > xCmdEnv; + ::ucbhelper::Content aContent( rURL, xCmdEnv ); + + bExists = aContent.isDocument(); + if (bExists) + { + Any aAny( aContent.getPropertyValue( A2OU( "IsReadOnly" ) ) ); + aAny >>= bRes; + } + } + catch (Exception &) + { + bRes = sal_True; + } + } + + if (pbExist) + *pbExist = bExists; + return bRes; +} + + + +static sal_Bool GetAltSpelling( sal_Int16 &rnChgPos, sal_Int16 &rnChgLen, OUString &rRplc, + uno::Reference< XHyphenatedWord > &rxHyphWord ) +{ + sal_Bool bRes = rxHyphWord->isAlternativeSpelling(); + if (bRes) + { + OUString aWord( rxHyphWord->getWord() ), + aHyphenatedWord( rxHyphWord->getHyphenatedWord() ); + sal_Int16 nHyphenationPos = rxHyphWord->getHyphenationPos(); + /*sal_Int16 nHyphenPos = rxHyphWord->getHyphenPos()*/; + const sal_Unicode *pWord = aWord.getStr(), + *pAltWord = aHyphenatedWord.getStr(); + + // at least char changes directly left or right to the hyphen + // should(!) be handled properly... + //! nHyphenationPos and nHyphenPos differ at most by 1 (see above) + //! Beware: eg "Schiffahrt" in German (pre spelling reform) + //! proves to be a bit nasty (nChgPosLeft and nChgPosRight overlap + //! to an extend.) + + // find first different char from left + sal_Int32 nPosL = 0, + nAltPosL = 0; + for (sal_Int16 i = 0 ; pWord[ nPosL ] == pAltWord[ nAltPosL ]; nPosL++, nAltPosL++, i++) + { + // restrict changes area beginning to the right to + // the char immediately following the hyphen. + //! serves to insert the additional "f" in "Schiffahrt" at + //! position 5 rather than position 6. + if (i >= nHyphenationPos + 1) + break; + } + + // find first different char from right + sal_Int32 nPosR = aWord.getLength() - 1, + nAltPosR = aHyphenatedWord.getLength() - 1; + for ( ; nPosR >= nPosL && nAltPosR >= nAltPosL + && pWord[ nPosR ] == pAltWord[ nAltPosR ]; + nPosR--, nAltPosR--) + ; + + rnChgPos = sal::static_int_cast< sal_Int16 >(nPosL); + rnChgLen = sal::static_int_cast< sal_Int16 >(nPosR - nPosL + 1); + DBG_ASSERT( rnChgLen >= 0, "nChgLen < 0"); + + sal_Int32 nTxtStart = nPosL; + sal_Int32 nTxtLen = nAltPosL - nPosL + 1; + rRplc = aHyphenatedWord.copy( nTxtStart, nTxtLen ); + } + return bRes; +} + + +static sal_Int16 GetOrigWordPos( const OUString &rOrigWord, sal_Int16 nPos ) +{ + sal_Int32 nLen = rOrigWord.getLength(); + sal_Int32 i = -1; + while (nPos >= 0 && i++ < nLen) + { + sal_Unicode cChar = rOrigWord[i]; + sal_Bool bSkip = IsHyphen( cChar ) || IsControlChar( cChar ); + if (!bSkip) + --nPos; + } + return sal::static_int_cast< sal_Int16 >((0 <= i && i < nLen) ? i : -1); +} + + +sal_Int32 GetPosInWordToCheck( const OUString &rTxt, sal_Int32 nPos ) +{ + sal_Int32 nRes = -1; + sal_Int32 nLen = rTxt.getLength(); + if (0 <= nPos && nPos < nLen) + { + nRes = 0; + for (sal_Int32 i = 0; i < nPos; ++i) + { + sal_Unicode cChar = rTxt[i]; + sal_Bool bSkip = IsHyphen( cChar ) || IsControlChar( cChar ); + if (!bSkip) + ++nRes; + } + } + return nRes; +} + + +uno::Reference< XHyphenatedWord > RebuildHyphensAndControlChars( + const OUString &rOrigWord, + uno::Reference< XHyphenatedWord > &rxHyphWord ) +{ + uno::Reference< XHyphenatedWord > xRes; + if (rOrigWord.getLength() && rxHyphWord.is()) + { + sal_Int16 nChgPos = 0, + nChgLen = 0; + OUString aRplc; + sal_Bool bAltSpelling = GetAltSpelling( nChgPos, nChgLen, aRplc, rxHyphWord ); +#if OSL_DEBUG_LEVEL > 1 + OUString aWord( rxHyphWord->getWord() ); +#endif + + OUString aOrigHyphenatedWord; + sal_Int16 nOrigHyphenPos = -1; + sal_Int16 nOrigHyphenationPos = -1; + if (!bAltSpelling) + { + aOrigHyphenatedWord = rOrigWord; + nOrigHyphenPos = GetOrigWordPos( rOrigWord, rxHyphWord->getHyphenPos() ); + nOrigHyphenationPos = GetOrigWordPos( rOrigWord, rxHyphWord->getHyphenationPos() ); + } + else + { + //! should at least work with the German words + //! B�-c-k-er and Sc-hif-fah-rt + + OUString aLeft, aRight; + sal_Int16 nPos = GetOrigWordPos( rOrigWord, nChgPos ); + + // get words like Sc-hif-fah-rt to work correct + sal_Int16 nHyphenationPos = rxHyphWord->getHyphenationPos(); + if (nChgPos > nHyphenationPos) + --nPos; + + aLeft = rOrigWord.copy( 0, nPos ); + aRight = rOrigWord.copy( nPos + nChgLen ); + + aOrigHyphenatedWord = aLeft; + aOrigHyphenatedWord += aRplc; + aOrigHyphenatedWord += aRight; + + nOrigHyphenPos = sal::static_int_cast< sal_Int16 >(aLeft.getLength() + + rxHyphWord->getHyphenPos() - nChgPos); + nOrigHyphenationPos = GetOrigWordPos( rOrigWord, nHyphenationPos ); + } + + if (nOrigHyphenPos == -1 || nOrigHyphenationPos == -1) + { + DBG_ASSERT( 0, "failed to get nOrigHyphenPos or nOrigHyphenationPos" ); + } + else + { + sal_Int16 nLang = LocaleToLanguage( rxHyphWord->getLocale() ); + xRes = new HyphenatedWord( + rOrigWord, nLang, nOrigHyphenationPos, + aOrigHyphenatedWord, nOrigHyphenPos ); + } + + } + return xRes; +} + + + + +static CharClass & lcl_GetCharClass() +{ + static CharClass aCC( CreateLocale( LANGUAGE_ENGLISH_US ) ); + return aCC; +} + + +osl::Mutex & lcl_GetCharClassMutex() +{ + static osl::Mutex aMutex; + return aMutex; +} + + +sal_Bool IsUpper( const String &rText, xub_StrLen nPos, xub_StrLen nLen, sal_Int16 nLanguage ) +{ + MutexGuard aGuard( lcl_GetCharClassMutex() ); + + CharClass &rCC = lcl_GetCharClass(); + rCC.setLocale( CreateLocale( nLanguage ) ); + sal_Int32 nFlags = rCC.getStringType( rText, nPos, nLen ); + return (nFlags & KCharacterType::UPPER) + && !(nFlags & KCharacterType::LOWER); +} + + +sal_Bool IsLower( const String &rText, xub_StrLen nPos, xub_StrLen nLen, sal_Int16 nLanguage ) +{ + MutexGuard aGuard( lcl_GetCharClassMutex() ); + + CharClass &rCC = lcl_GetCharClass(); + rCC.setLocale( CreateLocale( nLanguage ) ); + sal_Int32 nFlags = rCC.getStringType( rText, nPos, nLen ); + return (nFlags & KCharacterType::LOWER) + && !(nFlags & KCharacterType::UPPER); +} + + +String ToLower( const String &rText, sal_Int16 nLanguage ) +{ + MutexGuard aGuard( lcl_GetCharClassMutex() ); + + CharClass &rCC = lcl_GetCharClass(); + rCC.setLocale( CreateLocale( nLanguage ) ); + return rCC.lower( rText ); +} + + +String ToUpper( const String &rText, sal_Int16 nLanguage ) +{ + MutexGuard aGuard( lcl_GetCharClassMutex() ); + + CharClass &rCC = lcl_GetCharClass(); + rCC.setLocale( CreateLocale( nLanguage ) ); + return rCC.upper( rText ); +} + + +String ToTitle( const String &rText, sal_Int16 nLanguage ) +{ + MutexGuard aGuard( lcl_GetCharClassMutex() ); + + CharClass &rCC = lcl_GetCharClass(); + rCC.setLocale( CreateLocale( nLanguage ) ); + return rCC.toTitle( rText, 0, rText.Len() ); +} + + +sal_Unicode ToLower( const sal_Unicode cChar, sal_Int16 nLanguage ) +{ + MutexGuard aGuard( lcl_GetCharClassMutex() ); + + CharClass &rCC = lcl_GetCharClass(); + rCC.setLocale( CreateLocale( nLanguage ) ); + return rCC.lower( cChar ).GetChar(0); +} + + +sal_Unicode ToUpper( const sal_Unicode cChar, sal_Int16 nLanguage ) +{ + MutexGuard aGuard( lcl_GetCharClassMutex() ); + + CharClass &rCC = lcl_GetCharClass(); + rCC.setLocale( CreateLocale( nLanguage ) ); + return rCC.upper( cChar ).GetChar(0); +} + +// sorted(!) array of unicode ranges for code points that are exclusively(!) used as numbers +// and thus may NOT not be part of names or words like the Chinese/Japanese number characters +static const sal_uInt32 the_aDigitZeroes [] = +{ + 0x00000030, //0039 ; Decimal # Nd [10] DIGIT ZERO..DIGIT NINE + 0x00000660, //0669 ; Decimal # Nd [10] ARABIC-INDIC DIGIT ZERO..ARABIC-INDIC DIGIT NINE + 0x000006F0, //06F9 ; Decimal # Nd [10] EXTENDED ARABIC-INDIC DIGIT ZERO..EXTENDED ARABIC-INDIC DIGIT NINE + 0x000007C0, //07C9 ; Decimal # Nd [10] NKO DIGIT ZERO..NKO DIGIT NINE + 0x00000966, //096F ; Decimal # Nd [10] DEVANAGARI DIGIT ZERO..DEVANAGARI DIGIT NINE + 0x000009E6, //09EF ; Decimal # Nd [10] BENGALI DIGIT ZERO..BENGALI DIGIT NINE + 0x00000A66, //0A6F ; Decimal # Nd [10] GURMUKHI DIGIT ZERO..GURMUKHI DIGIT NINE + 0x00000AE6, //0AEF ; Decimal # Nd [10] GUJARATI DIGIT ZERO..GUJARATI DIGIT NINE + 0x00000B66, //0B6F ; Decimal # Nd [10] ORIYA DIGIT ZERO..ORIYA DIGIT NINE + 0x00000BE6, //0BEF ; Decimal # Nd [10] TAMIL DIGIT ZERO..TAMIL DIGIT NINE + 0x00000C66, //0C6F ; Decimal # Nd [10] TELUGU DIGIT ZERO..TELUGU DIGIT NINE + 0x00000CE6, //0CEF ; Decimal # Nd [10] KANNADA DIGIT ZERO..KANNADA DIGIT NINE + 0x00000D66, //0D6F ; Decimal # Nd [10] MALAYALAM DIGIT ZERO..MALAYALAM DIGIT NINE + 0x00000E50, //0E59 ; Decimal # Nd [10] THAI DIGIT ZERO..THAI DIGIT NINE + 0x00000ED0, //0ED9 ; Decimal # Nd [10] LAO DIGIT ZERO..LAO DIGIT NINE + 0x00000F20, //0F29 ; Decimal # Nd [10] TIBETAN DIGIT ZERO..TIBETAN DIGIT NINE + 0x00001040, //1049 ; Decimal # Nd [10] MYANMAR DIGIT ZERO..MYANMAR DIGIT NINE + 0x00001090, //1099 ; Decimal # Nd [10] MYANMAR SHAN DIGIT ZERO..MYANMAR SHAN DIGIT NINE + 0x000017E0, //17E9 ; Decimal # Nd [10] KHMER DIGIT ZERO..KHMER DIGIT NINE + 0x00001810, //1819 ; Decimal # Nd [10] MONGOLIAN DIGIT ZERO..MONGOLIAN DIGIT NINE + 0x00001946, //194F ; Decimal # Nd [10] LIMBU DIGIT ZERO..LIMBU DIGIT NINE + 0x000019D0, //19D9 ; Decimal # Nd [10] NEW TAI LUE DIGIT ZERO..NEW TAI LUE DIGIT NINE + 0x00001B50, //1B59 ; Decimal # Nd [10] BALINESE DIGIT ZERO..BALINESE DIGIT NINE + 0x00001BB0, //1BB9 ; Decimal # Nd [10] SUNDANESE DIGIT ZERO..SUNDANESE DIGIT NINE + 0x00001C40, //1C49 ; Decimal # Nd [10] LEPCHA DIGIT ZERO..LEPCHA DIGIT NINE + 0x00001C50, //1C59 ; Decimal # Nd [10] OL CHIKI DIGIT ZERO..OL CHIKI DIGIT NINE + 0x0000A620, //A629 ; Decimal # Nd [10] VAI DIGIT ZERO..VAI DIGIT NINE + 0x0000A8D0, //A8D9 ; Decimal # Nd [10] SAURASHTRA DIGIT ZERO..SAURASHTRA DIGIT NINE + 0x0000A900, //A909 ; Decimal # Nd [10] KAYAH LI DIGIT ZERO..KAYAH LI DIGIT NINE + 0x0000AA50, //AA59 ; Decimal # Nd [10] CHAM DIGIT ZERO..CHAM DIGIT NINE + 0x0000FF10, //FF19 ; Decimal # Nd [10] FULLWIDTH DIGIT ZERO..FULLWIDTH DIGIT NINE + 0x000104A0, //104A9 ; Decimal # Nd [10] OSMANYA DIGIT ZERO..OSMANYA DIGIT NINE + 0x0001D7CE //1D7FF ; Decimal # Nd [50] MATHEMATICAL BOLD DIGIT ZERO..MATHEMATICAL MONOSPACE DIGIT NINE +}; + +sal_Bool HasDigits( const OUString &rText ) +{ + static const int nNumDigitZeroes = SAL_N_ELEMENTS(the_aDigitZeroes); + const sal_Int32 nLen = rText.getLength(); + + sal_Int32 i = 0; + while (i < nLen) // for all characters ... + { + const sal_uInt32 nCodePoint = rText.iterateCodePoints( &i ); // handle unicode surrogates correctly... + for (int j = 0; j < nNumDigitZeroes; ++j) // ... check in all 0..9 ranges + { + sal_uInt32 nDigitZero = the_aDigitZeroes[ j ]; + if (nDigitZero > nCodePoint) + break; + if (/*nDigitZero <= nCodePoint &&*/ nCodePoint <= nDigitZero + 9) + return sal_True; + } + } + return sal_False; +} + + +sal_Bool IsNumeric( const String &rText ) +{ + sal_Bool bRes = sal_False; + xub_StrLen nLen = rText.Len(); + if (nLen) + { + bRes = sal_True; + xub_StrLen i = 0; + while (i < nLen) + { + sal_Unicode cChar = rText.GetChar( i++ ); + if ( !((sal_Unicode)'0' <= cChar && cChar <= (sal_Unicode)'9') ) + { + bRes = sal_False; + break; + } + } + } + return bRes; +} + + + +uno::Reference< XInterface > GetOneInstanceService( const char *pServiceName ) +{ + uno::Reference< XInterface > xRef; + + if (pServiceName) + { + uno::Reference< XMultiServiceFactory > xMgr( getProcessServiceFactory() ); + if (xMgr.is()) + { + try + { + xRef = xMgr->createInstance( ::rtl::OUString::createFromAscii( pServiceName ) ); + } + catch (uno::Exception &) + { + DBG_ASSERT( 0, "createInstance failed" ); + } + } + } + + return xRef; +} + +uno::Reference< XPropertySet > GetLinguProperties() +{ + return uno::Reference< XPropertySet > ( + GetOneInstanceService( SN_LINGU_PROPERTIES ), UNO_QUERY ); +} + +uno::Reference< XSearchableDictionaryList > GetSearchableDictionaryList() +{ + return uno::Reference< XSearchableDictionaryList > ( + GetOneInstanceService( SN_DICTIONARY_LIST ), UNO_QUERY ); +} + +uno::Reference< XDictionaryList > GetDictionaryList() +{ + return uno::Reference< XDictionaryList > ( + GetOneInstanceService( SN_DICTIONARY_LIST ), UNO_QUERY ); +} + +uno::Reference< XDictionary > GetIgnoreAllList() +{ + uno::Reference< XDictionary > xRes; + uno::Reference< XDictionaryList > xDL( GetDictionaryList() ); + if (xDL.is()) + xRes = xDL->getDictionaryByName( A2OU("IgnoreAllList") ); + return xRes; +} + + +AppExitListener::AppExitListener() +{ + // add object to Desktop EventListeners in order to properly call + // the AtExit function at appliction exit. + uno::Reference< XMultiServiceFactory > xMgr = getProcessServiceFactory(); + + if (xMgr.is()) + { + try + { + xDesktop = uno::Reference< frame::XDesktop >( + xMgr->createInstance( A2OU( SN_DESKTOP ) ), UNO_QUERY ); + } + catch (uno::Exception &) + { + DBG_ASSERT( 0, "createInstance failed" ); + } + } +} + +AppExitListener::~AppExitListener() +{ +} + + +void AppExitListener::Activate() +{ + if (xDesktop.is()) + xDesktop->addTerminateListener( this ); +} + + +void AppExitListener::Deactivate() +{ + if (xDesktop.is()) + xDesktop->removeTerminateListener( this ); +} + + +void SAL_CALL + AppExitListener::disposing( const EventObject& rEvtSource ) + throw(RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + + if (xDesktop.is() && rEvtSource.Source == xDesktop) + { + xDesktop = NULL; //! release reference to desktop + } +} + + +void SAL_CALL + AppExitListener::queryTermination( const EventObject& /*rEvtSource*/ ) + throw(frame::TerminationVetoException, RuntimeException) +{ +} + + +void SAL_CALL + AppExitListener::notifyTermination( const EventObject& rEvtSource ) + throw(RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + + if (xDesktop.is() && rEvtSource.Source == xDesktop) + { + AtExit(); + } +} + + +} // namespace linguistic + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/linguistic/source/misc2.cxx b/linguistic/source/misc2.cxx new file mode 100644 index 000000000000..4bfcb74e9551 --- /dev/null +++ b/linguistic/source/misc2.cxx @@ -0,0 +1,175 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_linguistic.hxx" +#include <tools/string.hxx> +#include <tools/fsys.hxx> +#include <tools/urlobj.hxx> +#include <ucbhelper/content.hxx> +#include <tools/debug.hxx> +#include <unotools/pathoptions.hxx> +#include <unotools/processfactory.hxx> +#include <unotools/localfilehelper.hxx> +#include <unotools/localedatawrapper.hxx> +#include <unotools/ucbhelper.hxx> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/beans/XFastPropertySet.hpp> + +#include <com/sun/star/beans/PropertyValues.hpp> +#include <com/sun/star/uno/Sequence.hxx> +#include <com/sun/star/uno/Reference.h> + +#include "linguistic/misc.hxx" + +using namespace com::sun::star; + +namespace linguistic +{ + + +sal_Bool FileExists( const String &rMainURL ) +{ + sal_Bool bExists = sal_False; + if (rMainURL.Len()) + { + try + { + ::ucbhelper::Content aContent( rMainURL, + uno::Reference< ::com::sun::star::ucb::XCommandEnvironment >()); + bExists = aContent.isDocument(); + } + catch (uno::Exception &) + { + } + } + return bExists; +} + +static uno::Sequence< rtl::OUString > GetMultiPaths_Impl( + const rtl::OUString &rPathPrefix, + sal_Int16 nPathFlags ) +{ + uno::Sequence< rtl::OUString > aRes; + uno::Sequence< rtl::OUString > aInternalPaths; + uno::Sequence< rtl::OUString > aUserPaths; + rtl::OUString aWritablePath; + + bool bSuccess = true; + uno::Reference< lang::XMultiServiceFactory > xMgr( utl::getProcessServiceFactory() ); + if (xMgr.is()) + { + try + { + String aInternal( rPathPrefix ); + String aUser( rPathPrefix ); + String aWriteable( rPathPrefix ); + aInternal .AppendAscii( "_internal" ); + aUser .AppendAscii( "_user" ); + aWriteable.AppendAscii( "_writable" ); + + uno::Reference< beans::XPropertySet > xPathSettings( xMgr->createInstance( + A2OU( "com.sun.star.util.PathSettings" ) ), uno::UNO_QUERY_THROW ); + xPathSettings->getPropertyValue( aInternal ) >>= aInternalPaths; + xPathSettings->getPropertyValue( aUser ) >>= aUserPaths; + xPathSettings->getPropertyValue( aWriteable ) >>= aWritablePath; + } + catch (uno::Exception &) + { + bSuccess = false; + } + } + if (bSuccess) + { + // build resulting sequence by adding the pathes in the following order: + // 1. writable path + // 2. all user pathes + // 3. all internal pathes + sal_Int32 nMaxEntries = aInternalPaths.getLength() + aUserPaths.getLength(); + if (aWritablePath.getLength() > 0) + ++nMaxEntries; + aRes.realloc( nMaxEntries ); + rtl::OUString *pRes = aRes.getArray(); + sal_Int32 nCount = 0; // number of actually added entries + if ((nPathFlags & PATH_FLAG_WRITABLE) && aWritablePath.getLength() != 0) + pRes[ nCount++ ] = aWritablePath; + for (int i = 0; i < 2; ++i) + { + const uno::Sequence< rtl::OUString > &rPathSeq = i == 0 ? aUserPaths : aInternalPaths; + const rtl::OUString *pPathSeq = rPathSeq.getConstArray(); + for (sal_Int32 k = 0; k < rPathSeq.getLength(); ++k) + { + const bool bAddUser = &rPathSeq == &aUserPaths && (nPathFlags & PATH_FLAG_USER); + const bool bAddInternal = &rPathSeq == &aInternalPaths && (nPathFlags & PATH_FLAG_INTERNAL); + if ((bAddUser || bAddInternal) && pPathSeq[k].getLength() > 0) + pRes[ nCount++ ] = pPathSeq[k]; + } + } + aRes.realloc( nCount ); + } + + return aRes; +} + +rtl::OUString GetDictionaryWriteablePath() +{ + uno::Sequence< rtl::OUString > aPaths( GetMultiPaths_Impl( A2OU("Dictionary"), PATH_FLAG_WRITABLE ) ); + DBG_ASSERT( aPaths.getLength() == 1, "Dictionary_writable path corrupted?" ); + String aRes; + if (aPaths.getLength() > 0) + aRes = aPaths[0]; + return aRes; +} + +uno::Sequence< rtl::OUString > GetDictionaryPaths( sal_Int16 nPathFlags ) +{ + return GetMultiPaths_Impl( A2OU("Dictionary"), nPathFlags ); +} + +String GetWritableDictionaryURL( const String &rDicName ) +{ + // new user writable dictionaries should be created in the 'writable' path + String aDirName( GetDictionaryWriteablePath() ); + + // build URL to use for a new (persistent) dictionary + INetURLObject aURLObj; + aURLObj.SetSmartProtocol( INET_PROT_FILE ); + aURLObj.SetSmartURL( aDirName ); + DBG_ASSERT(!aURLObj.HasError(), "lng : invalid URL"); + aURLObj.Append( rDicName, INetURLObject::ENCODE_ALL ); + DBG_ASSERT(!aURLObj.HasError(), "lng : invalid URL"); + + // NO_DECODE preserves the escape sequences that might be included in aDirName + // depending on the characters used in the path string. (Needed when comparing + // the dictionary URL with GetDictionaryWriteablePath in DicList::createDictionary.) + return aURLObj.GetMainURL( INetURLObject::NO_DECODE ); +} + +} // namespace linguistic + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/linguistic/source/spelldsp.cxx b/linguistic/source/spelldsp.cxx new file mode 100644 index 000000000000..97f96ce227b4 --- /dev/null +++ b/linguistic/source/spelldsp.cxx @@ -0,0 +1,855 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_linguistic.hxx" + +#include <com/sun/star/uno/Reference.h> +#include <com/sun/star/linguistic2/XSearchableDictionaryList.hpp> +#include <com/sun/star/linguistic2/SpellFailure.hpp> +#include <com/sun/star/registry/XRegistryKey.hpp> + +#include <cppuhelper/factory.hxx> // helper for factories +#include <unotools/localedatawrapper.hxx> +#include <unotools/processfactory.hxx> +#include <tools/debug.hxx> +#include <svl/lngmisc.hxx> +#include <osl/mutex.hxx> + +#include <vector> + +#include "spelldsp.hxx" +#include "linguistic/spelldta.hxx" +#include "lngsvcmgr.hxx" +#include "linguistic/lngprops.hxx" + + +using namespace utl; +using namespace osl; +using namespace com::sun::star; +using namespace com::sun::star::beans; +using namespace com::sun::star::lang; +using namespace com::sun::star::uno; +using namespace com::sun::star::linguistic2; +using namespace linguistic; + +using ::rtl::OUString; + +// ProposalList: list of proposals for misspelled words +// The order of strings in the array should be left unchanged because the +// spellchecker should have put the more likely suggestions at the top. +// New entries will be added to the end but duplicates are to be avoided. +// Removing entries is done by assigning the empty string. +// The sequence is constructed from all non empty strings in the original +// while maintaining the order. +class ProposalList +{ + std::vector< OUString > aVec; + + sal_Bool HasEntry( const OUString &rText ) const; + + // make copy c-tor and assignment operator private + ProposalList( const ProposalList & ); + ProposalList & operator = ( const ProposalList & ); + +public: + ProposalList() {} + + size_t Count() const; + void Prepend( const OUString &rText ); + void Append( const OUString &rNew ); + void Append( const std::vector< OUString > &rNew ); + void Append( const Sequence< OUString > &rNew ); + void Remove( const OUString &rText ); + Sequence< OUString > GetSequence() const; +}; + + +sal_Bool ProposalList::HasEntry( const OUString &rText ) const +{ + sal_Bool bFound = sal_False; + size_t nCnt = aVec.size(); + for (size_t i = 0; !bFound && i < nCnt; ++i) + { + if (aVec[i] == rText) + bFound = sal_True; + } + return bFound; +} + +void ProposalList::Prepend( const OUString &rText ) +{ + if (!HasEntry( rText )) + aVec.insert( aVec.begin(), rText ); +} + +void ProposalList::Append( const OUString &rText ) +{ + if (!HasEntry( rText )) + aVec.push_back( rText ); +} + +void ProposalList::Append( const std::vector< OUString > &rNew ) +{ + size_t nLen = rNew.size(); + for ( size_t i = 0; i < nLen; ++i) + { + const OUString &rText = rNew[i]; + if (!HasEntry( rText )) + Append( rText ); + } +} + +void ProposalList::Append( const Sequence< OUString > &rNew ) +{ + sal_Int32 nLen = rNew.getLength(); + const OUString *pNew = rNew.getConstArray(); + for (sal_Int32 i = 0; i < nLen; ++i) + { + const OUString &rText = pNew[i]; + if (!HasEntry( rText )) + Append( rText ); + } +} + +size_t ProposalList::Count() const +{ + // returns the number of non-empty strings in the vector + + size_t nRes = 0; + size_t nLen = aVec.size(); + for (size_t i = 0; i < nLen; ++i) + { + if (aVec[i].getLength() != 0) + ++nRes; + } + return nRes; +} + +Sequence< OUString > ProposalList::GetSequence() const +{ + sal_Int32 nCount = Count(); + sal_Int32 nIdx = 0; + Sequence< OUString > aRes( nCount ); + OUString *pRes = aRes.getArray(); + sal_Int32 nLen = aVec.size(); + for (sal_Int32 i = 0; i < nLen; ++i) + { + const OUString &rText = aVec[i]; + DBG_ASSERT( nIdx < nCount, "index our of range" ); + if (nIdx < nCount && rText.getLength() > 0) + pRes[ nIdx++ ] = rText; + } + return aRes; +} + +void ProposalList::Remove( const OUString &rText ) +{ + size_t nLen = aVec.size(); + for (size_t i = 0; i < nLen; ++i) + { + OUString &rEntry = aVec[i]; + if (rEntry == rText) + { + rEntry = OUString(); + break; // there should be only one matching entry + } + } +} + +sal_Bool SvcListHasLanguage( + const LangSvcEntries_Spell &rEntry, + LanguageType nLanguage ) +{ + sal_Bool bHasLanguage = sal_False; + Locale aTmpLocale; + + const Reference< XSpellChecker > *pRef = rEntry.aSvcRefs .getConstArray(); + sal_Int32 nLen = rEntry.aSvcRefs.getLength(); + for (sal_Int32 k = 0; k < nLen && !bHasLanguage; ++k) + { + if (pRef[k].is()) + { + if (0 == aTmpLocale.Language.getLength()) + aTmpLocale = CreateLocale( nLanguage ); + bHasLanguage = pRef[k]->hasLocale( aTmpLocale ); + } + } + + return bHasLanguage; +} + +SpellCheckerDispatcher::SpellCheckerDispatcher( LngSvcMgr &rLngSvcMgr ) : + rMgr (rLngSvcMgr) +{ + pCache = NULL; +} + + +SpellCheckerDispatcher::~SpellCheckerDispatcher() +{ + ClearSvcList(); + delete pCache; +} + + +void SpellCheckerDispatcher::ClearSvcList() +{ + // release memory for each table entry + SpellSvcByLangMap_t aTmp; + aSvcMap.swap( aTmp ); +} + + +Sequence< Locale > SAL_CALL SpellCheckerDispatcher::getLocales() + throw(RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + + Sequence< Locale > aLocales( static_cast< sal_Int32 >(aSvcMap.size()) ); + Locale *pLocales = aLocales.getArray(); + SpellSvcByLangMap_t::const_iterator aIt; + for (aIt = aSvcMap.begin(); aIt != aSvcMap.end(); ++aIt) + { + *pLocales++ = CreateLocale( aIt->first ); + } + return aLocales; +} + + +sal_Bool SAL_CALL SpellCheckerDispatcher::hasLocale( const Locale& rLocale ) + throw(RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + SpellSvcByLangMap_t::const_iterator aIt( aSvcMap.find( LocaleToLanguage( rLocale ) ) ); + return aIt != aSvcMap.end(); +} + + +sal_Bool SAL_CALL + SpellCheckerDispatcher::isValid( const OUString& rWord, const Locale& rLocale, + const PropertyValues& rProperties ) + throw(IllegalArgumentException, RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + return isValid_Impl( rWord, LocaleToLanguage( rLocale ), rProperties, sal_True ); +} + + +Reference< XSpellAlternatives > SAL_CALL + SpellCheckerDispatcher::spell( const OUString& rWord, const Locale& rLocale, + const PropertyValues& rProperties ) + throw(IllegalArgumentException, RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + return spell_Impl( rWord, LocaleToLanguage( rLocale ), rProperties, sal_True ); +} + + +// returns the overall result of cross-checking with all user-dictionaries +// including the IgnoreAll list +static Reference< XDictionaryEntry > lcl_GetRulingDictionaryEntry( + const OUString &rWord, + LanguageType nLanguage ) +{ + Reference< XDictionaryEntry > xRes; + + // the order of winning from top to bottom is: + // 1) IgnoreAll list will always win + // 2) Negative dictionaries will win over positive dictionaries + Reference< XDictionary > xIgnoreAll( GetIgnoreAllList() ); + if (xIgnoreAll.is()) + xRes = xIgnoreAll->getEntry( rWord ); + if (!xRes.is()) + { + Reference< XDictionaryList > xDList( GetDictionaryList() ); + Reference< XDictionaryEntry > xNegEntry( SearchDicList( xDList, + rWord, nLanguage, sal_False, sal_True ) ); + if (xNegEntry.is()) + xRes = xNegEntry; + else + { + Reference< XDictionaryEntry > xPosEntry( SearchDicList( xDList, + rWord, nLanguage, sal_True, sal_True ) ); + if (xPosEntry.is()) + xRes = xPosEntry; + } + } + + return xRes; +} + + +sal_Bool SpellCheckerDispatcher::isValid_Impl( + const OUString& rWord, + LanguageType nLanguage, + const PropertyValues& rProperties, + sal_Bool bCheckDics) + throw( RuntimeException, IllegalArgumentException ) +{ + MutexGuard aGuard( GetLinguMutex() ); + + sal_Bool bRes = sal_True; + + if (nLanguage == LANGUAGE_NONE || !rWord.getLength()) + return bRes; + + // search for entry with that language + SpellSvcByLangMap_t::iterator aIt( aSvcMap.find( nLanguage ) ); + LangSvcEntries_Spell *pEntry = aIt != aSvcMap.end() ? aIt->second.get() : NULL; + + if (!pEntry) + { +#ifdef LINGU_EXCEPTIONS + throw IllegalArgumentException(); +#endif + } + else + { + OUString aChkWord( rWord ); + Locale aLocale( CreateLocale( nLanguage ) ); + + // replace typographical apostroph by ascii apostroph + String aSingleQuote( GetLocaleDataWrapper( nLanguage ).getQuotationMarkEnd() ); + DBG_ASSERT( 1 == aSingleQuote.Len(), "unexpectend length of quotation mark" ); + if (aSingleQuote.Len()) + aChkWord = aChkWord.replace( aSingleQuote.GetChar(0), '\'' ); + + RemoveHyphens( aChkWord ); + if (IsIgnoreControlChars( rProperties, GetPropSet() )) + RemoveControlChars( aChkWord ); + + sal_Int32 nLen = pEntry->aSvcRefs.getLength(); + DBG_ASSERT( nLen == pEntry->aSvcImplNames.getLength(), + "lng : sequence length mismatch"); + DBG_ASSERT( pEntry->nLastTriedSvcIndex < nLen, + "lng : index out of range"); + + sal_Int32 i = 0; + sal_Bool bTmpRes = sal_True; + sal_Bool bTmpResValid = sal_False; + + // try already instantiated services first + { + const Reference< XSpellChecker > *pRef = + pEntry->aSvcRefs.getConstArray(); + while (i <= pEntry->nLastTriedSvcIndex + && (!bTmpResValid || sal_False == bTmpRes)) + { + bTmpResValid = sal_True; + if (pRef[i].is() && pRef[i]->hasLocale( aLocale )) + { + bTmpRes = GetCache().CheckWord( aChkWord, nLanguage ); + if (!bTmpRes) + { + bTmpRes = pRef[i]->isValid( aChkWord, aLocale, rProperties ); + + // Add correct words to the cache. + // But not those that are correct only because of + // the temporary supplied settings. + if (bTmpRes && 0 == rProperties.getLength()) + GetCache().AddWord( aChkWord, nLanguage ); + } + } + else + bTmpResValid = sal_False; + + if (bTmpResValid) + bRes = bTmpRes; + + ++i; + } + } + + // if still no result instantiate new services and try those + if ((!bTmpResValid || sal_False == bTmpRes) + && pEntry->nLastTriedSvcIndex < nLen - 1) + { + const OUString *pImplNames = pEntry->aSvcImplNames.getConstArray(); + Reference< XSpellChecker > *pRef = pEntry->aSvcRefs .getArray(); + + Reference< XMultiServiceFactory > xMgr( getProcessServiceFactory() ); + if (xMgr.is()) + { + // build service initialization argument + Sequence< Any > aArgs(2); + aArgs.getArray()[0] <<= GetPropSet(); + + while (i < nLen && (!bTmpResValid || sal_False == bTmpRes)) + { + // create specific service via it's implementation name + Reference< XSpellChecker > xSpell; + try + { + xSpell = Reference< XSpellChecker >( + xMgr->createInstanceWithArguments( + pImplNames[i], aArgs ), UNO_QUERY ); + } + catch (uno::Exception &) + { + DBG_ASSERT( 0, "createInstanceWithArguments failed" ); + } + pRef [i] = xSpell; + + Reference< XLinguServiceEventBroadcaster > + xBroadcaster( xSpell, UNO_QUERY ); + if (xBroadcaster.is()) + rMgr.AddLngSvcEvtBroadcaster( xBroadcaster ); + + bTmpResValid = sal_True; + if (xSpell.is() && xSpell->hasLocale( aLocale )) + { + bTmpRes = GetCache().CheckWord( aChkWord, nLanguage ); + if (!bTmpRes) + { + bTmpRes = xSpell->isValid( aChkWord, aLocale, rProperties ); + + // Add correct words to the cache. + // But not those that are correct only because of + // the temporary supplied settings. + if (bTmpRes && 0 == rProperties.getLength()) + GetCache().AddWord( aChkWord, nLanguage ); + } + } + else + bTmpResValid = sal_False; + + if (bTmpResValid) + bRes = bTmpRes; + + pEntry->nLastTriedSvcIndex = (sal_Int16) i; + ++i; + } + + // if language is not supported by any of the services + // remove it from the list. + if (i == nLen) + { + if (!SvcListHasLanguage( *pEntry, nLanguage )) + aSvcMap.erase( nLanguage ); + } + } + } + + // cross-check against results from dictionaries which have precedence! + if (bCheckDics && + GetDicList().is() && IsUseDicList( rProperties, GetPropSet() )) + { + Reference< XDictionaryEntry > xTmp( lcl_GetRulingDictionaryEntry( aChkWord, nLanguage ) ); + if (xTmp.is()) + bRes = !xTmp->isNegative(); + } + } + + return bRes; +} + + +Reference< XSpellAlternatives > SpellCheckerDispatcher::spell_Impl( + const OUString& rWord, + LanguageType nLanguage, + const PropertyValues& rProperties, + sal_Bool bCheckDics ) + throw(IllegalArgumentException, RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + + Reference< XSpellAlternatives > xRes; + + if (nLanguage == LANGUAGE_NONE || !rWord.getLength()) + return xRes; + + // search for entry with that language + SpellSvcByLangMap_t::iterator aIt( aSvcMap.find( nLanguage ) ); + LangSvcEntries_Spell *pEntry = aIt != aSvcMap.end() ? aIt->second.get() : NULL; + + if (!pEntry) + { +#ifdef LINGU_EXCEPTIONS + throw IllegalArgumentException(); +#endif + } + else + { + OUString aChkWord( rWord ); + Locale aLocale( CreateLocale( nLanguage ) ); + + // replace typographical apostroph by ascii apostroph + String aSingleQuote( GetLocaleDataWrapper( nLanguage ).getQuotationMarkEnd() ); + DBG_ASSERT( 1 == aSingleQuote.Len(), "unexpectend length of quotation mark" ); + if (aSingleQuote.Len()) + aChkWord = aChkWord.replace( aSingleQuote.GetChar(0), '\'' ); + + RemoveHyphens( aChkWord ); + if (IsIgnoreControlChars( rProperties, GetPropSet() )) + RemoveControlChars( aChkWord ); + + sal_Int32 nLen = pEntry->aSvcRefs.getLength(); + DBG_ASSERT( nLen == pEntry->aSvcImplNames.getLength(), + "lng : sequence length mismatch"); + DBG_ASSERT( pEntry->nLastTriedSvcIndex < nLen, + "lng : index out of range"); + + sal_Int32 i = 0; + Reference< XSpellAlternatives > xTmpRes; + sal_Bool bTmpResValid = sal_False; + + // try already instantiated services first + { + const Reference< XSpellChecker > *pRef = pEntry->aSvcRefs.getConstArray(); + sal_Int32 nNumSugestions = -1; + while (i <= pEntry->nLastTriedSvcIndex + && (!bTmpResValid || xTmpRes.is()) ) + { + bTmpResValid = sal_True; + if (pRef[i].is() && pRef[i]->hasLocale( aLocale )) + { + sal_Bool bOK = GetCache().CheckWord( aChkWord, nLanguage ); + if (bOK) + xTmpRes = NULL; + else + { + xTmpRes = pRef[i]->spell( aChkWord, aLocale, rProperties ); + + // Add correct words to the cache. + // But not those that are correct only because of + // the temporary supplied settings. + if (!xTmpRes.is() && 0 == rProperties.getLength()) + GetCache().AddWord( aChkWord, nLanguage ); + } + } + else + bTmpResValid = sal_False; + + // return first found result if the word is not known by any checker. + // But if that result has no suggestions use the first one that does + // provide suggestions for the misspelled word. + if (!xRes.is() && bTmpResValid) + { + xRes = xTmpRes; + nNumSugestions = 0; + if (xRes.is()) + nNumSugestions = xRes->getAlternatives().getLength(); + } + sal_Int32 nTmpNumSugestions = 0; + if (xTmpRes.is() && bTmpResValid) + nTmpNumSugestions = xTmpRes->getAlternatives().getLength(); + if (xRes.is() && nNumSugestions == 0 && nTmpNumSugestions > 0) + { + xRes = xTmpRes; + nNumSugestions = nTmpNumSugestions; + } + + ++i; + } + } + + // if still no result instantiate new services and try those + if ((!bTmpResValid || xTmpRes.is()) + && pEntry->nLastTriedSvcIndex < nLen - 1) + { + const OUString *pImplNames = pEntry->aSvcImplNames.getConstArray(); + Reference< XSpellChecker > *pRef = pEntry->aSvcRefs .getArray(); + + Reference< XMultiServiceFactory > xMgr( getProcessServiceFactory() ); + if (xMgr.is()) + { + // build service initialization argument + Sequence< Any > aArgs(2); + aArgs.getArray()[0] <<= GetPropSet(); + + sal_Int32 nNumSugestions = -1; + while (i < nLen && (!bTmpResValid || xTmpRes.is())) + { + // create specific service via it's implementation name + Reference< XSpellChecker > xSpell; + try + { + xSpell = Reference< XSpellChecker >( + xMgr->createInstanceWithArguments( + pImplNames[i], aArgs ), UNO_QUERY ); + } + catch (uno::Exception &) + { + DBG_ASSERT( 0, "createInstanceWithArguments failed" ); + } + pRef [i] = xSpell; + + Reference< XLinguServiceEventBroadcaster > + xBroadcaster( xSpell, UNO_QUERY ); + if (xBroadcaster.is()) + rMgr.AddLngSvcEvtBroadcaster( xBroadcaster ); + + bTmpResValid = sal_True; + if (xSpell.is() && xSpell->hasLocale( aLocale )) + { + sal_Bool bOK = GetCache().CheckWord( aChkWord, nLanguage ); + if (bOK) + xTmpRes = NULL; + else + { + xTmpRes = xSpell->spell( aChkWord, aLocale, rProperties ); + + // Add correct words to the cache. + // But not those that are correct only because of + // the temporary supplied settings. + if (!xTmpRes.is() && 0 == rProperties.getLength()) + GetCache().AddWord( aChkWord, nLanguage ); + } + } + else + bTmpResValid = sal_False; + + // return first found result if the word is not known by any checker. + // But if that result has no suggestions use the first one that does + // provide suggestions for the misspelled word. + if (!xRes.is() && bTmpResValid) + { + xRes = xTmpRes; + nNumSugestions = 0; + if (xRes.is()) + nNumSugestions = xRes->getAlternatives().getLength(); + } + sal_Int32 nTmpNumSugestions = 0; + if (xTmpRes.is() && bTmpResValid) + nTmpNumSugestions = xTmpRes->getAlternatives().getLength(); + if (xRes.is() && nNumSugestions == 0 && nTmpNumSugestions > 0) + { + xRes = xTmpRes; + nNumSugestions = nTmpNumSugestions; + } + + pEntry->nLastTriedSvcIndex = (sal_Int16) i; + ++i; + } + + // if language is not supported by any of the services + // remove it from the list. + if (i == nLen) + { + if (!SvcListHasLanguage( *pEntry, nLanguage )) + aSvcMap.erase( nLanguage ); + } + } + } + + // if word is finally found to be correct + // clear previously remembered alternatives + if (bTmpResValid && !xTmpRes.is()) + xRes = NULL; + + // list of proposals found (to be checked against entries of + // neagtive dictionaries) + ProposalList aProposalList; + sal_Int16 eFailureType = -1; // no failure + if (xRes.is()) + { + aProposalList.Append( xRes->getAlternatives() ); + eFailureType = xRes->getFailureType(); + } + Reference< XDictionaryList > xDList; + if (GetDicList().is() && IsUseDicList( rProperties, GetPropSet() )) + xDList = Reference< XDictionaryList >( GetDicList(), UNO_QUERY ); + + // cross-check against results from user-dictionaries which have precedence! + if (bCheckDics && xDList.is()) + { + Reference< XDictionaryEntry > xTmp( lcl_GetRulingDictionaryEntry( aChkWord, nLanguage ) ); + if (xTmp.is()) + { + if (xTmp->isNegative()) // positive entry found + { + eFailureType = SpellFailure::IS_NEGATIVE_WORD; + + // replacement text to be added to suggestions, if not empty + OUString aAddRplcTxt( xTmp->getReplacementText() ); + + // replacement text must not be in negative dictionary itself + if (aAddRplcTxt.getLength() && + !SearchDicList( xDList, aAddRplcTxt, nLanguage, sal_False, sal_True ).is()) + { + aProposalList.Prepend( aAddRplcTxt ); + } + } + else // positive entry found + { + xRes = NULL; + eFailureType = -1; // no failure + } + } + } + + if (eFailureType != -1) // word misspelled or found in negative user-dictionary + { + // search suitable user-dictionaries for suggestions that are + // similar to the misspelled word + std::vector< OUString > aDicListProps; // list of proposals from user-dictionaries + SearchSimilarText( aChkWord, nLanguage, xDList, aDicListProps ); + aProposalList.Append( aDicListProps ); + Sequence< OUString > aProposals = aProposalList.GetSequence(); + + // remove entries listed in negative dictionaries + // (we don't want to display suggestions that will be regarded as misspelledlater on) + if (bCheckDics && xDList.is()) + SeqRemoveNegEntries( aProposals, xDList, nLanguage ); + + uno::Reference< linguistic2::XSetSpellAlternatives > xSetAlt( xRes, uno::UNO_QUERY ); + if (xSetAlt.is()) + { + xSetAlt->setAlternatives( aProposals ); + xSetAlt->setFailureType( eFailureType ); + } + else + { + if (xRes.is()) + { + DBG_ASSERT( 0, "XSetSpellAlternatives not implemented!" ); + } + else if (aProposals.getLength() > 0) + { + // no xRes but Proposals found from the user-dictionaries. + // Thus we need to create an xRes... + xRes = new linguistic::SpellAlternatives( rWord, nLanguage, + SpellFailure::IS_NEGATIVE_WORD, aProposals ); + } + } + } + } + + return xRes; +} + +uno::Sequence< sal_Int16 > SAL_CALL SpellCheckerDispatcher::getLanguages( ) +throw (uno::RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + uno::Sequence< Locale > aTmp( getLocales() ); + uno::Sequence< sal_Int16 > aRes( LocaleSeqToLangSeq( aTmp ) ); + return aRes; +} + + +sal_Bool SAL_CALL SpellCheckerDispatcher::hasLanguage( + sal_Int16 nLanguage ) +throw (uno::RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + Locale aLocale( CreateLocale( nLanguage ) ); + return hasLocale( aLocale ); +} + + +sal_Bool SAL_CALL SpellCheckerDispatcher::isValid( + const OUString& rWord, + sal_Int16 nLanguage, + const uno::Sequence< beans::PropertyValue >& rProperties ) +throw (lang::IllegalArgumentException, uno::RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + Locale aLocale( CreateLocale( nLanguage ) ); + return isValid( rWord, aLocale, rProperties); +} + + +uno::Reference< linguistic2::XSpellAlternatives > SAL_CALL SpellCheckerDispatcher::spell( + const OUString& rWord, + sal_Int16 nLanguage, + const uno::Sequence< beans::PropertyValue >& rProperties ) +throw (lang::IllegalArgumentException, uno::RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + Locale aLocale( CreateLocale( nLanguage ) ); + return spell( rWord, aLocale, rProperties); +} + + +void SpellCheckerDispatcher::SetServiceList( const Locale &rLocale, + const Sequence< OUString > &rSvcImplNames ) +{ + MutexGuard aGuard( GetLinguMutex() ); + + if (pCache) + pCache->Flush(); // new services may spell differently... + + sal_Int16 nLanguage = LocaleToLanguage( rLocale ); + + sal_Int32 nLen = rSvcImplNames.getLength(); + if (0 == nLen) + // remove entry + aSvcMap.erase( nLanguage ); + else + { + // modify/add entry + LangSvcEntries_Spell *pEntry = aSvcMap[ nLanguage ].get(); + if (pEntry) + { + pEntry->Clear(); + pEntry->aSvcImplNames = rSvcImplNames; + pEntry->aSvcRefs = Sequence< Reference < XSpellChecker > > ( nLen ); + } + else + { + boost::shared_ptr< LangSvcEntries_Spell > pTmpEntry( new LangSvcEntries_Spell( rSvcImplNames ) ); + pTmpEntry->aSvcRefs = Sequence< Reference < XSpellChecker > >( nLen ); + aSvcMap[ nLanguage ] = pTmpEntry; + } + } +} + + +Sequence< OUString > + SpellCheckerDispatcher::GetServiceList( const Locale &rLocale ) const +{ + MutexGuard aGuard( GetLinguMutex() ); + + Sequence< OUString > aRes; + + // search for entry with that language and use data from that + sal_Int16 nLanguage = LocaleToLanguage( rLocale ); + SpellCheckerDispatcher *pThis = (SpellCheckerDispatcher *) this; + const SpellSvcByLangMap_t::iterator aIt( pThis->aSvcMap.find( nLanguage ) ); + const LangSvcEntries_Spell *pEntry = aIt != aSvcMap.end() ? aIt->second.get() : NULL; + if (pEntry) + aRes = pEntry->aSvcImplNames; + + return aRes; +} + + +LinguDispatcher::DspType SpellCheckerDispatcher::GetDspType() const +{ + return DSP_SPELL; +} + +void SpellCheckerDispatcher::FlushSpellCache() +{ + if (pCache) + pCache->Flush(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/linguistic/source/spelldsp.hxx b/linguistic/source/spelldsp.hxx new file mode 100644 index 000000000000..9ae9cd45456b --- /dev/null +++ b/linguistic/source/spelldsp.hxx @@ -0,0 +1,163 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _LINGUISTIC_SPELLDSP_HXX_ +#define _LINGUISTIC_SPELLDSP_HXX_ + +#include "lngopt.hxx" +#include "linguistic/misc.hxx" +#include "iprcache.hxx" + +#include <uno/lbnames.h> // CPPU_CURRENT_LANGUAGE_BINDING_NAME macro, which specify the environment type +#include <cppuhelper/implbase1.hxx> +#include <cppuhelper/implbase2.hxx> +#include <cppuhelper/implbase7.hxx> +#include <com/sun/star/lang/XComponent.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/lang/XServiceDisplayName.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/beans/PropertyValues.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/linguistic2/XSpellChecker1.hpp> +#include <com/sun/star/linguistic2/XSpellChecker.hpp> +#include <com/sun/star/linguistic2/XSearchableDictionaryList.hpp> +#include <com/sun/star/linguistic2/XLinguServiceEventBroadcaster.hpp> + +#include <boost/shared_ptr.hpp> +#include <map> + +class LngSvcMgr; + + +class SpellCheckerDispatcher : + public cppu::WeakImplHelper2 + < + ::com::sun::star::linguistic2::XSpellChecker1, + ::com::sun::star::linguistic2::XSpellChecker + >, + public LinguDispatcher +{ + typedef boost::shared_ptr< LangSvcEntries_Spell > LangSvcEntries_Spell_Ptr_t; + typedef std::map< LanguageType, LangSvcEntries_Spell_Ptr_t > SpellSvcByLangMap_t; + SpellSvcByLangMap_t aSvcMap; + LinguOptions aOpt; + + ::com::sun::star::uno::Reference< + ::com::sun::star::beans::XPropertySet > xPropSet; + ::com::sun::star::uno::Reference< + ::com::sun::star::linguistic2::XSearchableDictionaryList > xDicList; + + LngSvcMgr &rMgr; + linguistic::SpellCache *pCache; // Spell Cache (holds known words) + + // disallow copy-constructor and assignment-operator for now + SpellCheckerDispatcher(const SpellCheckerDispatcher &); + SpellCheckerDispatcher & operator = (const SpellCheckerDispatcher &); + + inline linguistic::SpellCache & GetCache() const; + + inline ::com::sun::star::uno::Reference< + ::com::sun::star::beans::XPropertySet > + GetPropSet(); + inline ::com::sun::star::uno::Reference< + ::com::sun::star::linguistic2::XSearchableDictionaryList > + GetDicList(); + + void ClearSvcList(); + + sal_Bool isValid_Impl(const ::rtl::OUString& aWord, LanguageType nLanguage, + const ::com::sun::star::beans::PropertyValues& aProperties, + sal_Bool bCheckDics) + throw( ::com::sun::star::uno::RuntimeException, ::com::sun::star::lang::IllegalArgumentException ); + + ::com::sun::star::uno::Reference< + ::com::sun::star::linguistic2::XSpellAlternatives > + spell_Impl(const ::rtl::OUString& aWord, LanguageType nLanguage, + const ::com::sun::star::beans::PropertyValues& aProperties, + sal_Bool bCheckDics) + throw( ::com::sun::star::uno::RuntimeException, ::com::sun::star::lang::IllegalArgumentException ); + +public: + SpellCheckerDispatcher( LngSvcMgr &rLngSvcMgr ); + virtual ~SpellCheckerDispatcher(); + + // XSupportedLocales (for XSpellChecker) + virtual ::com::sun::star::uno::Sequence< ::com::sun::star::lang::Locale > SAL_CALL getLocales() throw(::com::sun::star::uno::RuntimeException); + virtual sal_Bool SAL_CALL hasLocale( const ::com::sun::star::lang::Locale& aLocale ) throw(::com::sun::star::uno::RuntimeException); + + // XSpellChecker + virtual sal_Bool SAL_CALL isValid( const ::rtl::OUString& aWord, const ::com::sun::star::lang::Locale& aLocale, const ::com::sun::star::beans::PropertyValues& aProperties ) throw(::com::sun::star::lang::IllegalArgumentException, ::com::sun::star::uno::RuntimeException); + virtual ::com::sun::star::uno::Reference< ::com::sun::star::linguistic2::XSpellAlternatives > SAL_CALL spell( const ::rtl::OUString& aWord, const ::com::sun::star::lang::Locale& aLocale, const ::com::sun::star::beans::PropertyValues& aProperties ) throw(::com::sun::star::lang::IllegalArgumentException, ::com::sun::star::uno::RuntimeException); + + // XSupportedLanguages + virtual ::com::sun::star::uno::Sequence< ::sal_Int16 > SAL_CALL getLanguages( ) throw (::com::sun::star::uno::RuntimeException); + virtual ::sal_Bool SAL_CALL hasLanguage( ::sal_Int16 nLanguage ) throw (::com::sun::star::uno::RuntimeException); + + // XSpellChecker1 + virtual ::sal_Bool SAL_CALL isValid( const ::rtl::OUString& aWord, ::sal_Int16 nLanguage, const ::com::sun::star::uno::Sequence< ::com::sun::star::beans::PropertyValue >& aProperties ) throw (::com::sun::star::lang::IllegalArgumentException, ::com::sun::star::uno::RuntimeException); + virtual ::com::sun::star::uno::Reference< ::com::sun::star::linguistic2::XSpellAlternatives > SAL_CALL spell( const ::rtl::OUString& aWord, ::sal_Int16 nLanguage, const ::com::sun::star::uno::Sequence< ::com::sun::star::beans::PropertyValue >& aProperties ) throw (::com::sun::star::lang::IllegalArgumentException, ::com::sun::star::uno::RuntimeException); + + // LinguDispatcher + virtual void SetServiceList( const ::com::sun::star::lang::Locale &rLocale, const ::com::sun::star::uno::Sequence< rtl::OUString > &rSvcImplNames ); + virtual ::com::sun::star::uno::Sequence< rtl::OUString > GetServiceList( const ::com::sun::star::lang::Locale &rLocale ) const; + virtual DspType GetDspType() const; + + void FlushSpellCache(); +}; + + +inline linguistic::SpellCache & SpellCheckerDispatcher::GetCache() const +{ + if (!pCache) + ((SpellCheckerDispatcher *) this)->pCache = new linguistic::SpellCache(); + return *pCache; +} + + +inline ::com::sun::star::uno::Reference< + ::com::sun::star::beans::XPropertySet > + SpellCheckerDispatcher::GetPropSet() +{ + return xPropSet.is() ? + xPropSet : xPropSet = linguistic::GetLinguProperties(); +} + + +inline ::com::sun::star::uno::Reference< + ::com::sun::star::linguistic2::XSearchableDictionaryList > + SpellCheckerDispatcher::GetDicList() +{ + return xDicList.is() ? + xDicList : xDicList = linguistic::GetSearchableDictionaryList(); +} + + + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/linguistic/source/spelldta.cxx b/linguistic/source/spelldta.cxx new file mode 100644 index 000000000000..c197fea767e6 --- /dev/null +++ b/linguistic/source/spelldta.cxx @@ -0,0 +1,323 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_linguistic.hxx" +#include <com/sun/star/uno/Reference.h> + +#include <com/sun/star/linguistic2/SpellFailure.hpp> +#include <com/sun/star/linguistic2/XSearchableDictionaryList.hpp> +#include <tools/debug.hxx> +#include <unotools/processfactory.hxx> +#include <osl/mutex.hxx> + +#include <vector> + +#include "linguistic/spelldta.hxx" +#include "lngsvcmgr.hxx" + + +using namespace utl; +using namespace osl; +using namespace com::sun::star; +using namespace com::sun::star::beans; +using namespace com::sun::star::lang; +using namespace com::sun::star::uno; +using namespace com::sun::star::linguistic2; + +using ::rtl::OUString; + +namespace linguistic +{ + +#define MAX_PROPOSALS 40 + +sal_Bool SeqHasEntry( + const Sequence< OUString > &rSeq, + const OUString &rTxt) +{ + sal_Bool bRes = sal_False; + sal_Int32 nLen = rSeq.getLength(); + const OUString *pEntry = rSeq.getConstArray(); + for (sal_Int32 i = 0; i < nLen && !bRes; ++i) + { + if (rTxt == pEntry[i]) + bRes = sal_True; + } + return bRes; +} + + +void SearchSimilarText( const OUString &rText, sal_Int16 nLanguage, + Reference< XDictionaryList > &xDicList, + std::vector< OUString > & rDicListProps ) +{ + if (!xDicList.is()) + return; + + const uno::Sequence< Reference< XDictionary > > + aDics( xDicList->getDictionaries() ); + const Reference< XDictionary > + *pDic = aDics.getConstArray(); + sal_Int32 nDics = xDicList->getCount(); + + for (sal_Int32 i = 0; i < nDics; i++) + { + Reference< XDictionary > xDic( pDic[i], UNO_QUERY ); + + sal_Int16 nLang = LocaleToLanguage( xDic->getLocale() ); + + if ( xDic.is() && xDic->isActive() + && (nLang == nLanguage || nLang == LANGUAGE_NONE) ) + { +#if OSL_DEBUG_LEVEL > 1 + DictionaryType eType = xDic->getDictionaryType(); + (void) eType; + DBG_ASSERT( eType != DictionaryType_MIXED, "unexpected dictionary type" ); +#endif + const Sequence< Reference< XDictionaryEntry > > aEntries = xDic->getEntries(); + const Reference< XDictionaryEntry > *pEntries = aEntries.getConstArray(); + sal_Int32 nLen = aEntries.getLength(); + for (sal_Int32 k = 0; k < nLen; ++k) + { + String aEntryTxt; + if (pEntries[k].is()) + { + aEntryTxt = pEntries[k]->getDictionaryWord(); + // remove characters used to determine hyphenation positions + aEntryTxt.EraseAllChars( '=' ); + } + if (aEntryTxt.Len() > 0 && LevDistance( rText, aEntryTxt ) <= 2) + rDicListProps.push_back( aEntryTxt ); + } + } + } +} + + +void SeqRemoveNegEntries( Sequence< OUString > &rSeq, + Reference< XDictionaryList > &rxDicList, + sal_Int16 nLanguage ) +{ + static const OUString aEmpty; + sal_Bool bSthRemoved = sal_False; + sal_Int32 nLen = rSeq.getLength(); + OUString *pEntries = rSeq.getArray(); + for (sal_Int32 i = 0; i < nLen; ++i) + { + Reference< XDictionaryEntry > xNegEntry( SearchDicList( rxDicList, + pEntries[i], nLanguage, sal_False, sal_True ) ); + if (xNegEntry.is()) + { + pEntries[i] = aEmpty; + bSthRemoved = sal_True; + } + } + if (bSthRemoved) + { + Sequence< OUString > aNew; + // merge sequence without duplicates and empty strings in new empty sequence + aNew = MergeProposalSeqs( aNew, rSeq, sal_False ); + rSeq = aNew; + } +} + + +Sequence< OUString > MergeProposalSeqs( + Sequence< OUString > &rAlt1, + Sequence< OUString > &rAlt2, + sal_Bool bAllowDuplicates ) +{ + Sequence< OUString > aMerged; + + if (0 == rAlt1.getLength() && bAllowDuplicates) + aMerged = rAlt2; + else if (0 == rAlt2.getLength() && bAllowDuplicates) + aMerged = rAlt1; + else + { + sal_Int32 nAltCount1 = rAlt1.getLength(); + const OUString *pAlt1 = rAlt1.getConstArray(); + sal_Int32 nAltCount2 = rAlt2.getLength(); + const OUString *pAlt2 = rAlt2.getConstArray(); + + sal_Int32 nCountNew = Min( nAltCount1 + nAltCount2, (sal_Int32) MAX_PROPOSALS ); + aMerged.realloc( nCountNew ); + OUString *pMerged = aMerged.getArray(); + + sal_Int32 nIndex = 0; + sal_Int32 i = 0; + for (int j = 0; j < 2; j++) + { + sal_Int32 nCount = j == 0 ? nAltCount1 : nAltCount2; + const OUString *pAlt = j == 0 ? pAlt1 : pAlt2; + for (i = 0; i < nCount && nIndex < MAX_PROPOSALS; i++) + { + if (pAlt[i].getLength() && + (bAllowDuplicates || !SeqHasEntry(aMerged, pAlt[i] ))) + pMerged[ nIndex++ ] = pAlt[ i ]; + } + } + aMerged.realloc( nIndex ); + } + + return aMerged; +} + + + +SpellAlternatives::SpellAlternatives() +{ + nLanguage = LANGUAGE_NONE; + nType = SpellFailure::IS_NEGATIVE_WORD; +} + + +SpellAlternatives::SpellAlternatives( + const OUString &rWord, sal_Int16 nLang, + sal_Int16 nFailureType, const OUString &rRplcWord ) : + aAlt ( Sequence< OUString >(1) ), + aWord (rWord), + nType (nFailureType), + nLanguage (nLang) +{ + if (rRplcWord.getLength()) + aAlt.getArray()[ 0 ] = rRplcWord; + else + aAlt.realloc( 0 ); +} + + +SpellAlternatives::SpellAlternatives( + const OUString &rWord, sal_Int16 nLang, sal_Int16 nFailureType, + const Sequence< OUString > &rAlternatives ) : + aAlt (rAlternatives), + aWord (rWord), + nType (nFailureType), + nLanguage (nLang) +{ +} + + +SpellAlternatives::~SpellAlternatives() +{ +} + + +OUString SAL_CALL SpellAlternatives::getWord() + throw(RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + return aWord; +} + + +Locale SAL_CALL SpellAlternatives::getLocale() + throw(RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + return CreateLocale( nLanguage ); +} + + +sal_Int16 SAL_CALL SpellAlternatives::getFailureType() + throw(RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + return nType; +} + + +sal_Int16 SAL_CALL SpellAlternatives::getAlternativesCount() + throw(RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + return (sal_Int16) aAlt.getLength(); +} + + +Sequence< OUString > SAL_CALL SpellAlternatives::getAlternatives() + throw(RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + return aAlt; +} + + +void SAL_CALL SpellAlternatives::setAlternatives( const uno::Sequence< OUString >& rAlternatives ) +throw (uno::RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + aAlt = rAlternatives; +} + + +void SAL_CALL SpellAlternatives::setFailureType( sal_Int16 nFailureType ) +throw (uno::RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + nType = nFailureType; +} + + +void SpellAlternatives::SetWordLanguage(const OUString &rWord, sal_Int16 nLang) +{ + MutexGuard aGuard( GetLinguMutex() ); + aWord = rWord; + nLanguage = nLang; +} + + +void SpellAlternatives::SetFailureType(sal_Int16 nTypeP) +{ + MutexGuard aGuard( GetLinguMutex() ); + nType = nTypeP; +} + + +void SpellAlternatives::SetAlternatives( const Sequence< OUString > &rAlt ) +{ + MutexGuard aGuard( GetLinguMutex() ); + aAlt = rAlt; +} + + +com::sun::star::uno::Reference < com::sun::star::linguistic2::XSpellAlternatives > SpellAlternatives::CreateSpellAlternatives( + const ::rtl::OUString &rWord, sal_Int16 nLang, sal_Int16 nTypeP, const ::com::sun::star::uno::Sequence< ::rtl::OUString > &rAlt ) +{ + SpellAlternatives* pAlt = new SpellAlternatives; + pAlt->SetWordLanguage( rWord, nLang ); + pAlt->SetFailureType( nTypeP ); + pAlt->SetAlternatives( rAlt ); + return Reference < XSpellAlternatives >(pAlt); +} + + +} // namespace linguistic + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/linguistic/source/thesdsp.cxx b/linguistic/source/thesdsp.cxx new file mode 100644 index 000000000000..7e4f72308597 --- /dev/null +++ b/linguistic/source/thesdsp.cxx @@ -0,0 +1,282 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_linguistic.hxx" +#include <i18npool/lang.h> +#include <tools/debug.hxx> +#include <svl/lngmisc.hxx> + +#include <cppuhelper/factory.hxx> // helper for factories +#include <com/sun/star/registry/XRegistryKey.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <unotools/processfactory.hxx> +#include <osl/mutex.hxx> + +#include "thesdsp.hxx" +#include "linguistic/lngprops.hxx" + +using namespace utl; +using namespace osl; +using namespace com::sun::star; +using namespace com::sun::star::beans; +using namespace com::sun::star::lang; +using namespace com::sun::star::uno; +using namespace com::sun::star::linguistic2; +using namespace linguistic; + +using ::rtl::OUString; + + +static sal_Bool SvcListHasLanguage( + const Sequence< Reference< XThesaurus > > &rRefs, + const Locale &rLocale ) +{ + sal_Bool bHasLanguage = sal_False; + + const Reference< XThesaurus > *pRef = rRefs.getConstArray(); + sal_Int32 nLen = rRefs.getLength(); + for (sal_Int32 k = 0; k < nLen && !bHasLanguage; ++k) + { + if (pRef[k].is()) + bHasLanguage = pRef[k]->hasLocale( rLocale ); + } + + return bHasLanguage; +} + + + +ThesaurusDispatcher::ThesaurusDispatcher() +{ +} + + +ThesaurusDispatcher::~ThesaurusDispatcher() +{ + ClearSvcList(); +} + + +void ThesaurusDispatcher::ClearSvcList() +{ + // release memory for each table entry + ThesSvcByLangMap_t aTmp; + aSvcMap.swap( aTmp ); +} + + +Sequence< Locale > SAL_CALL + ThesaurusDispatcher::getLocales() + throw(RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + + Sequence< Locale > aLocales( static_cast< sal_Int32 >(aSvcMap.size()) ); + Locale *pLocales = aLocales.getArray(); + ThesSvcByLangMap_t::const_iterator aIt; + for (aIt = aSvcMap.begin(); aIt != aSvcMap.end(); ++aIt) + { + *pLocales++ = CreateLocale( aIt->first ); + } + return aLocales; +} + + +sal_Bool SAL_CALL + ThesaurusDispatcher::hasLocale( const Locale& rLocale ) + throw(RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + ThesSvcByLangMap_t::const_iterator aIt( aSvcMap.find( LocaleToLanguage( rLocale ) ) ); + return aIt != aSvcMap.end(); +} + + +Sequence< Reference< XMeaning > > SAL_CALL + ThesaurusDispatcher::queryMeanings( + const OUString& rTerm, const Locale& rLocale, + const PropertyValues& rProperties ) + throw(IllegalArgumentException, RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + + Sequence< Reference< XMeaning > > aMeanings; + + sal_Int16 nLanguage = LocaleToLanguage( rLocale ); + if (nLanguage == LANGUAGE_NONE || !rTerm.getLength()) + return aMeanings; + + // search for entry with that language + ThesSvcByLangMap_t::iterator aIt( aSvcMap.find( nLanguage ) ); + LangSvcEntries_Thes *pEntry = aIt != aSvcMap.end() ? aIt->second.get() : NULL; + + if (!pEntry) + { +#ifdef LINGU_EXCEPTIONS + throw IllegalArgumentException(); +#endif + } + else + { + OUString aChkWord( rTerm ); + aChkWord = aChkWord.replace( SVT_HARD_SPACE, ' ' ); + RemoveHyphens( aChkWord ); + if (IsIgnoreControlChars( rProperties, GetPropSet() )) + RemoveControlChars( aChkWord ); + + sal_Int32 nLen = pEntry->aSvcRefs.getLength(); + DBG_ASSERT( nLen == pEntry->aSvcImplNames.getLength(), + "lng : sequence length mismatch"); + DBG_ASSERT( pEntry->nLastTriedSvcIndex < nLen, + "lng : index out of range"); + + sal_Int32 i = 0; + + // try already instantiated services first + { + const Reference< XThesaurus > *pRef = pEntry->aSvcRefs.getConstArray(); + while (i <= pEntry->nLastTriedSvcIndex + && aMeanings.getLength() == 0) + { + if (pRef[i].is() && pRef[i]->hasLocale( rLocale )) + aMeanings = pRef[i]->queryMeanings( aChkWord, rLocale, rProperties ); + ++i; + } + } + + // if still no result instantiate new services and try those + if (aMeanings.getLength() == 0 + && pEntry->nLastTriedSvcIndex < nLen - 1) + { + const OUString *pImplNames = pEntry->aSvcImplNames.getConstArray(); + Reference< XThesaurus > *pRef = pEntry->aSvcRefs.getArray(); + + Reference< XMultiServiceFactory > xMgr( getProcessServiceFactory() ); + if (xMgr.is()) + { + // build service initialization argument + Sequence< Any > aArgs(1); + aArgs.getArray()[0] <<= GetPropSet(); + + while (i < nLen && aMeanings.getLength() == 0) + { + // create specific service via it's implementation name + Reference< XThesaurus > xThes; + try + { + xThes = Reference< XThesaurus >( + xMgr->createInstanceWithArguments( + pImplNames[i], aArgs ), UNO_QUERY ); + } + catch (uno::Exception &) + { + DBG_ASSERT( 0, "createInstanceWithArguments failed" ); + } + pRef[i] = xThes; + + if (xThes.is() && xThes->hasLocale( rLocale )) + aMeanings = xThes->queryMeanings( aChkWord, rLocale, rProperties ); + + pEntry->nLastTriedSvcIndex = (sal_Int16) i; + ++i; + } + + // if language is not supported by any of the services + // remove it from the list. + if (i == nLen && aMeanings.getLength() == 0) + { + if (!SvcListHasLanguage( pEntry->aSvcRefs, rLocale )) + aSvcMap.erase( nLanguage ); + } + } + } + } + + return aMeanings; +} + + +void ThesaurusDispatcher::SetServiceList( const Locale &rLocale, + const Sequence< OUString > &rSvcImplNames ) +{ + MutexGuard aGuard( GetLinguMutex() ); + + sal_Int16 nLanguage = LocaleToLanguage( rLocale ); + + sal_Int32 nLen = rSvcImplNames.getLength(); + if (0 == nLen) + // remove entry + aSvcMap.erase( nLanguage ); + else + { + // modify/add entry + LangSvcEntries_Thes *pEntry = aSvcMap[ nLanguage ].get(); + if (pEntry) + { + pEntry->Clear(); + pEntry->aSvcImplNames = rSvcImplNames; + pEntry->aSvcRefs = Sequence< Reference < XThesaurus > >( nLen ); + } + else + { + boost::shared_ptr< LangSvcEntries_Thes > pTmpEntry( new LangSvcEntries_Thes( rSvcImplNames ) ); + pTmpEntry->aSvcRefs = Sequence< Reference < XThesaurus > >( nLen ); + aSvcMap[ nLanguage ] = pTmpEntry; + } + } +} + + +Sequence< OUString > + ThesaurusDispatcher::GetServiceList( const Locale &rLocale ) const +{ + MutexGuard aGuard( GetLinguMutex() ); + + Sequence< OUString > aRes; + + // search for entry with that language and use data from that + sal_Int16 nLanguage = LocaleToLanguage( rLocale ); + ThesaurusDispatcher *pThis = (ThesaurusDispatcher *) this; + const ThesSvcByLangMap_t::iterator aIt( pThis->aSvcMap.find( nLanguage ) ); + const LangSvcEntries_Thes *pEntry = aIt != aSvcMap.end() ? aIt->second.get() : NULL; + if (pEntry) + aRes = pEntry->aSvcImplNames; + + return aRes; +} + + +LinguDispatcher::DspType ThesaurusDispatcher::GetDspType() const +{ + return DSP_THES; +} + + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/linguistic/source/thesdsp.hxx b/linguistic/source/thesdsp.hxx new file mode 100644 index 000000000000..dabd278a8926 --- /dev/null +++ b/linguistic/source/thesdsp.hxx @@ -0,0 +1,129 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _LINGUISTIC_THESDSP_HXX_ +#define _LINGUISTIC_THESDSP_HXX_ + +#include <com/sun/star/uno/Reference.h> +#include <com/sun/star/uno/Sequence.h> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/beans/XPropertyAccess.hpp> +#include <com/sun/star/beans/XPropertyChangeListener.hpp> +#include <com/sun/star/lang/XComponent.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/lang/XServiceDisplayName.hpp> + +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/linguistic2/XThesaurus.hpp> + +#include <uno/lbnames.h> // CPPU_CURRENT_LANGUAGE_BINDING_NAME macro, which specify the environment type +#include <cppuhelper/implbase1.hxx> // helper for implementations +#include <cppuhelper/implbase5.hxx> // helper for implementations +#include <cppuhelper/interfacecontainer.h> + +#include <osl/mutex.hxx> + +#include <boost/shared_ptr.hpp> +#include <map> + +#include "lngopt.hxx" + + + +class ThesaurusDispatcher : + public cppu::WeakImplHelper1 + < + ::com::sun::star::linguistic2::XThesaurus + >, + public LinguDispatcher +{ + typedef boost::shared_ptr< LangSvcEntries_Thes > LangSvcEntries_Thes_Ptr_t; + typedef std::map< LanguageType, LangSvcEntries_Thes_Ptr_t > ThesSvcByLangMap_t; + ThesSvcByLangMap_t aSvcMap; + + ::com::sun::star::uno::Reference< + ::com::sun::star::beans::XPropertySet > xPropSet; + + // disallow copy-constructor and assignment-operator for now + ThesaurusDispatcher(const ThesaurusDispatcher &); + ThesaurusDispatcher & operator = (const ThesaurusDispatcher &); + + inline ::com::sun::star::uno::Reference< + ::com::sun::star::beans::XPropertySet > + GetPropSet(); + + void ClearSvcList(); + +public: + ThesaurusDispatcher(); + virtual ~ThesaurusDispatcher(); + + // XSupportedLocales + virtual ::com::sun::star::uno::Sequence< + ::com::sun::star::lang::Locale > SAL_CALL + getLocales() + throw(::com::sun::star::uno::RuntimeException); + virtual sal_Bool SAL_CALL + hasLocale( const ::com::sun::star::lang::Locale& aLocale ) + throw(::com::sun::star::uno::RuntimeException); + + // XThesaurus + virtual ::com::sun::star::uno::Sequence< + ::com::sun::star::uno::Reference< + ::com::sun::star::linguistic2::XMeaning > > SAL_CALL + queryMeanings( const ::rtl::OUString& aTerm, + const ::com::sun::star::lang::Locale& aLocale, + const ::com::sun::star::beans::PropertyValues& aProperties ) + throw(::com::sun::star::lang::IllegalArgumentException, + ::com::sun::star::uno::RuntimeException); + + // LinguDispatcher + virtual void + SetServiceList( const ::com::sun::star::lang::Locale &rLocale, + const ::com::sun::star::uno::Sequence< + rtl::OUString > &rSvcImplNames ); + virtual ::com::sun::star::uno::Sequence< rtl::OUString > + GetServiceList( const ::com::sun::star::lang::Locale &rLocale ) const; + virtual DspType + GetDspType() const; +}; + + +inline ::com::sun::star::uno::Reference< + ::com::sun::star::beans::XPropertySet > + ThesaurusDispatcher::GetPropSet() +{ + return xPropSet.is() ? + xPropSet : xPropSet = linguistic::GetLinguProperties(); +} + + + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/linguistic/source/thesdta.cxx b/linguistic/source/thesdta.cxx new file mode 100644 index 000000000000..218e5ce2af89 --- /dev/null +++ b/linguistic/source/thesdta.cxx @@ -0,0 +1,82 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_linguistic.hxx" +#include <tools/debug.hxx> +#include <osl/mutex.hxx> + +#include <linguistic/misc.hxx> + +#include "thesdta.hxx" + +using namespace osl; +using namespace com::sun::star; +using namespace com::sun::star::beans; +using namespace com::sun::star::lang; +using namespace com::sun::star::uno; +using namespace com::sun::star::linguistic2; +using namespace linguistic; + +using ::rtl::OUString; + +namespace linguistic +{ + + + +ThesaurusMeaning::ThesaurusMeaning(const OUString &rText, + const OUString &rLookUpText, sal_Int16 nLookUpLang ) : + aText (rText), + aLookUpText (rLookUpText), + nLookUpLanguage (nLookUpLang) +{ +} + +ThesaurusMeaning::~ThesaurusMeaning() +{ +} + +OUString SAL_CALL + ThesaurusMeaning::getMeaning() throw(RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + return aText; +} + +/* +uno::Sequence< OUString > SAL_CALL ThesaurusMeaning::querySynonyms() + throw(RuntimeException) +{ +} +*/ + + +} // namespace linguistic + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |