lok: ensure that dialog windows are focused before emitting events.
[LibreOffice.git] / linguistic / source / convdic.cxx
blob3e9ec1ba7152535f4c804b626649641377094a10
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include <cppuhelper/factory.hxx>
22 #include <i18nlangtag/lang.h>
23 #include <i18nlangtag/languagetag.hxx>
24 #include <osl/mutex.hxx>
25 #include <sal/log.hxx>
26 #include <tools/debug.hxx>
27 #include <tools/stream.hxx>
28 #include <tools/urlobj.hxx>
29 #include <ucbhelper/content.hxx>
30 #include <comphelper/processfactory.hxx>
31 #include <comphelper/sequence.hxx>
32 #include <cppuhelper/supportsservice.hxx>
33 #include <unotools/streamwrap.hxx>
34 #include <unotools/ucbstreamhelper.hxx>
36 #include <com/sun/star/linguistic2/ConversionPropertyType.hpp>
37 #include <com/sun/star/util/XFlushable.hpp>
38 #include <com/sun/star/lang/Locale.hpp>
39 #include <com/sun/star/lang/EventObject.hpp>
40 #include <com/sun/star/ucb/SimpleFileAccess.hpp>
41 #include <com/sun/star/uno/Reference.h>
42 #include <com/sun/star/registry/XRegistryKey.hpp>
43 #include <com/sun/star/util/XFlushListener.hpp>
44 #include <com/sun/star/io/IOException.hpp>
45 #include <com/sun/star/io/XActiveDataSource.hpp>
46 #include <com/sun/star/io/XInputStream.hpp>
47 #include <com/sun/star/io/XOutputStream.hpp>
48 #include <com/sun/star/xml/sax/Writer.hpp>
49 #include <com/sun/star/document/XFilter.hpp>
50 #include <com/sun/star/beans/PropertyValue.hpp>
51 #include <com/sun/star/xml/sax/InputSource.hpp>
52 #include <com/sun/star/xml/sax/Parser.hpp>
53 #include <com/sun/star/xml/sax/SAXParseException.hpp>
54 #include <com/sun/star/container/NoSuchElementException.hpp>
55 #include <com/sun/star/container/ElementExistException.hpp>
58 #include "convdic.hxx"
59 #include "convdicxml.hxx"
60 #include <linguistic/misc.hxx>
61 #include "defs.hxx"
63 using namespace std;
64 using namespace utl;
65 using namespace osl;
66 using namespace com::sun::star;
67 using namespace com::sun::star::lang;
68 using namespace com::sun::star::uno;
69 using namespace com::sun::star::linguistic2;
70 using namespace linguistic;
73 #define SN_CONV_DICTIONARY "com.sun.star.linguistic2.ConversionDictionary"
75 static void ReadThroughDic( const OUString &rMainURL, ConvDicXMLImport &rImport )
77 if (rMainURL.isEmpty())
78 return;
79 DBG_ASSERT(!INetURLObject( rMainURL ).HasError(), "invalid URL");
81 uno::Reference< uno::XComponentContext > xContext( comphelper::getProcessComponentContext() );
83 // get xInputStream stream
84 uno::Reference< io::XInputStream > xIn;
85 try
87 uno::Reference< ucb::XSimpleFileAccess3 > xAccess( ucb::SimpleFileAccess::create(xContext) );
88 xIn = xAccess->openFileRead( rMainURL );
90 catch (const uno::Exception &)
92 SAL_WARN( "linguistic", "failed to get input stream" );
94 if (!xIn.is())
95 return;
97 SvStreamPtr pStream( utl::UcbStreamHelper::CreateStream( xIn ) );
99 // prepare ParserInputSource
100 xml::sax::InputSource aParserInput;
101 aParserInput.aInputStream = xIn;
103 // get parser
104 uno::Reference< xml::sax::XParser > xParser = xml::sax::Parser::create( xContext );
106 //!! keep a reference until everything is done to
107 //!! ensure the proper lifetime of the object
108 uno::Reference < xml::sax::XDocumentHandler > xFilter(
109 static_cast<xml::sax::XExtendedDocumentHandler *>(&rImport), UNO_QUERY );
111 // connect parser and filter
112 xParser->setDocumentHandler( xFilter );
114 // finally, parser the stream
117 xParser->parseStream( aParserInput ); // implicitly calls ConvDicXMLImport::CreateContext
119 catch( xml::sax::SAXParseException& )
122 catch( xml::sax::SAXException& )
125 catch( io::IOException& )
130 bool IsConvDic( const OUString &rFileURL, LanguageType &nLang, sal_Int16 &nConvType )
132 bool bRes = false;
134 if (rFileURL.isEmpty())
135 return bRes;
137 // check if file extension matches CONV_DIC_EXT
138 OUString aExt;
139 sal_Int32 nPos = rFileURL.lastIndexOf( '.' );
140 if (-1 != nPos)
141 aExt = rFileURL.copy( nPos + 1 ).toAsciiLowerCase();
142 if (aExt != CONV_DIC_EXT)
143 return bRes;
145 // first argument being 0 should stop the file from being parsed
146 // up to the end (reading all entries) when the required
147 // data (language, conversion type) is found.
148 rtl::Reference<ConvDicXMLImport> pImport = new ConvDicXMLImport( nullptr );
150 ReadThroughDic( rFileURL, *pImport ); // will implicitly add the entries
151 bRes = !LinguIsUnspecified( pImport->GetLanguage()) &&
152 pImport->GetConversionType() != -1;
153 DBG_ASSERT( bRes, "conversion dictionary corrupted?" );
155 if (bRes)
157 nLang = pImport->GetLanguage();
158 nConvType = pImport->GetConversionType();
161 return bRes;
165 ConvDic::ConvDic(
166 const OUString &rName,
167 LanguageType nLang,
168 sal_Int16 nConvType,
169 bool bBiDirectional,
170 const OUString &rMainURL) :
171 aFlushListeners( GetLinguMutex() )
173 aName = rName;
174 nLanguage = nLang;
175 nConversionType = nConvType;
176 aMainURL = rMainURL;
178 if (bBiDirectional)
179 pFromRight.reset( new ConvMap );
180 if (nLang == LANGUAGE_CHINESE_SIMPLIFIED || nLang == LANGUAGE_CHINESE_TRADITIONAL)
181 pConvPropType.reset( new PropTypeMap );
183 nMaxLeftCharCount = nMaxRightCharCount = 0;
184 bMaxCharCountIsValid = true;
186 bNeedEntries = true;
187 bIsModified = bIsActive = false;
189 if( !rMainURL.isEmpty() )
191 bool bExists = false;
192 IsReadOnly( rMainURL, &bExists );
194 if( !bExists ) // new empty dictionary
196 bNeedEntries = false;
197 //! create physical representation of an **empty** dictionary
198 //! that could be found by the dictionary-list implementation
199 // (Note: empty dictionaries are not just empty files!)
200 Save();
203 else
205 bNeedEntries = false;
210 ConvDic::~ConvDic()
215 void ConvDic::Load()
217 DBG_ASSERT( !bIsModified, "dictionary is modified. Really do 'Load'?" );
219 //!! prevent function from being called recursively via HasEntry, AddEntry
220 bNeedEntries = false;
221 rtl::Reference<ConvDicXMLImport> pImport = new ConvDicXMLImport( this );
222 ReadThroughDic( aMainURL, *pImport ); // will implicitly add the entries
223 bIsModified = false;
227 void ConvDic::Save()
229 DBG_ASSERT( !bNeedEntries, "saving while entries missing" );
230 if (aMainURL.isEmpty() || bNeedEntries)
231 return;
232 DBG_ASSERT(!INetURLObject( aMainURL ).HasError(), "invalid URL");
234 uno::Reference< uno::XComponentContext > xContext( comphelper::getProcessComponentContext() );
236 // get XOutputStream stream
237 uno::Reference< io::XStream > xStream;
240 uno::Reference< ucb::XSimpleFileAccess3 > xAccess( ucb::SimpleFileAccess::create(xContext) );
241 xStream = xAccess->openFileReadWrite( aMainURL );
243 catch (const uno::Exception &)
245 SAL_WARN( "linguistic", "failed to get input stream" );
247 if (!xStream.is())
248 return;
250 SvStreamPtr pStream( utl::UcbStreamHelper::CreateStream( xStream ) );
252 // get XML writer
253 uno::Reference< xml::sax::XWriter > xSaxWriter = xml::sax::Writer::create(xContext);
255 if (xStream.is())
257 // connect XML writer to output stream
258 xSaxWriter->setOutputStream( xStream->getOutputStream() );
260 // prepare arguments (prepend doc handler to given arguments)
261 rtl::Reference<ConvDicXMLExport> pExport = new ConvDicXMLExport( *this, aMainURL, xSaxWriter );
262 bool bRet = pExport->Export(); // write entries to file
263 DBG_ASSERT( !pStream->GetError(), "I/O error while writing to stream" );
264 if (bRet)
265 bIsModified = false;
267 DBG_ASSERT( !bIsModified, "dictionary still modified after save. Save failed?" );
271 ConvMap::iterator ConvDic::GetEntry( ConvMap &rMap, const OUString &rFirstText, const OUString &rSecondText )
273 pair< ConvMap::iterator, ConvMap::iterator > aRange =
274 rMap.equal_range( rFirstText );
275 ConvMap::iterator aPos = rMap.end();
276 for (ConvMap::iterator aIt = aRange.first;
277 aIt != aRange.second && aPos == rMap.end();
278 ++aIt)
280 if ((*aIt).second == rSecondText)
281 aPos = aIt;
283 return aPos;
287 bool ConvDic::HasEntry( const OUString &rLeftText, const OUString &rRightText )
289 if (bNeedEntries)
290 Load();
291 ConvMap::iterator aIt = GetEntry( aFromLeft, rLeftText, rRightText );
292 return aIt != aFromLeft.end();
296 void ConvDic::AddEntry( const OUString &rLeftText, const OUString &rRightText )
298 if (bNeedEntries)
299 Load();
301 DBG_ASSERT(!HasEntry( rLeftText, rRightText), "entry already exists" );
302 aFromLeft .emplace( rLeftText, rRightText );
303 if (pFromRight)
304 pFromRight->emplace( rRightText, rLeftText );
306 if (bMaxCharCountIsValid)
308 if (rLeftText.getLength() > nMaxLeftCharCount)
309 nMaxLeftCharCount = static_cast<sal_Int16>(rLeftText.getLength());
310 if (pFromRight.get() && rRightText.getLength() > nMaxRightCharCount)
311 nMaxRightCharCount = static_cast<sal_Int16>(rRightText.getLength());
314 bIsModified = true;
318 void ConvDic::RemoveEntry( const OUString &rLeftText, const OUString &rRightText )
320 if (bNeedEntries)
321 Load();
323 ConvMap::iterator aLeftIt = GetEntry( aFromLeft, rLeftText, rRightText );
324 DBG_ASSERT( aLeftIt != aFromLeft.end(), "left map entry missing" );
325 aFromLeft .erase( aLeftIt );
327 if (pFromRight)
329 ConvMap::iterator aRightIt = GetEntry( *pFromRight, rRightText, rLeftText );
330 DBG_ASSERT( aRightIt != pFromRight->end(), "right map entry missing" );
331 pFromRight->erase( aRightIt );
334 bIsModified = true;
335 bMaxCharCountIsValid = false;
339 OUString SAL_CALL ConvDic::getName( )
341 MutexGuard aGuard( GetLinguMutex() );
342 return aName;
346 Locale SAL_CALL ConvDic::getLocale( )
348 MutexGuard aGuard( GetLinguMutex() );
349 return LanguageTag::convertToLocale( nLanguage );
353 sal_Int16 SAL_CALL ConvDic::getConversionType( )
355 MutexGuard aGuard( GetLinguMutex() );
356 return nConversionType;
360 void SAL_CALL ConvDic::setActive( sal_Bool bActivate )
362 MutexGuard aGuard( GetLinguMutex() );
363 bIsActive = bActivate;
367 sal_Bool SAL_CALL ConvDic::isActive( )
369 MutexGuard aGuard( GetLinguMutex() );
370 return bIsActive;
374 void SAL_CALL ConvDic::clear( )
376 MutexGuard aGuard( GetLinguMutex() );
377 aFromLeft .clear();
378 if (pFromRight)
379 pFromRight->clear();
380 bNeedEntries = false;
381 bIsModified = true;
382 nMaxLeftCharCount = 0;
383 nMaxRightCharCount = 0;
384 bMaxCharCountIsValid = true;
388 uno::Sequence< OUString > SAL_CALL ConvDic::getConversions(
389 const OUString& aText,
390 sal_Int32 nStartPos,
391 sal_Int32 nLength,
392 ConversionDirection eDirection,
393 sal_Int32 /*nTextConversionOptions*/ )
395 MutexGuard aGuard( GetLinguMutex() );
397 if (!pFromRight && eDirection == ConversionDirection_FROM_RIGHT)
398 return uno::Sequence< OUString >();
400 if (bNeedEntries)
401 Load();
403 OUString aLookUpText( aText.copy(nStartPos, nLength) );
404 ConvMap &rConvMap = eDirection == ConversionDirection_FROM_LEFT ?
405 aFromLeft : *pFromRight;
406 pair< ConvMap::iterator, ConvMap::iterator > aRange =
407 rConvMap.equal_range( aLookUpText );
409 std::vector<OUString> aRes;
410 auto nCount = static_cast<size_t>(std::distance(aRange.first, aRange.second));
411 aRes.reserve(nCount);
413 std::transform(aRange.first, aRange.second, std::back_inserter(aRes),
414 [](ConvMap::const_reference rEntry) { return rEntry.second; });
416 return comphelper::containerToSequence(aRes);
420 uno::Sequence< OUString > SAL_CALL ConvDic::getConversionEntries(
421 ConversionDirection eDirection )
423 MutexGuard aGuard( GetLinguMutex() );
425 if (!pFromRight && eDirection == ConversionDirection_FROM_RIGHT)
426 return uno::Sequence< OUString >();
428 if (bNeedEntries)
429 Load();
431 ConvMap &rConvMap = eDirection == ConversionDirection_FROM_LEFT ?
432 aFromLeft : *pFromRight;
433 std::vector<OUString> aRes;
434 aRes.reserve(rConvMap.size());
435 for (auto const& elem : rConvMap)
437 OUString aCurEntry( elem.first );
438 // skip duplicate entries ( duplicate = duplicate entries
439 // respective to the evaluated side (FROM_LEFT or FROM_RIGHT).
440 // Thus if FROM_LEFT is evaluated for pairs (A,B) and (A,C)
441 // only one entry for A will be returned in the result)
442 if (std::find(aRes.begin(), aRes.end(), aCurEntry) == aRes.end())
443 aRes.push_back(aCurEntry);
446 return comphelper::containerToSequence(aRes);
450 void SAL_CALL ConvDic::addEntry(
451 const OUString& aLeftText,
452 const OUString& aRightText )
454 MutexGuard aGuard( GetLinguMutex() );
455 if (bNeedEntries)
456 Load();
457 if (HasEntry( aLeftText, aRightText ))
458 throw container::ElementExistException();
459 AddEntry( aLeftText, aRightText );
463 void SAL_CALL ConvDic::removeEntry(
464 const OUString& aLeftText,
465 const OUString& aRightText )
467 MutexGuard aGuard( GetLinguMutex() );
468 if (bNeedEntries)
469 Load();
470 if (!HasEntry( aLeftText, aRightText ))
471 throw container::NoSuchElementException();
472 RemoveEntry( aLeftText, aRightText );
476 sal_Int16 SAL_CALL ConvDic::getMaxCharCount( ConversionDirection eDirection )
478 MutexGuard aGuard( GetLinguMutex() );
480 if (!pFromRight && eDirection == ConversionDirection_FROM_RIGHT)
482 DBG_ASSERT( nMaxRightCharCount == 0, "max right char count should be 0" );
483 return 0;
486 if (bNeedEntries)
487 Load();
489 if (!bMaxCharCountIsValid)
491 nMaxLeftCharCount = 0;
492 for (auto const& elem : aFromLeft)
494 sal_Int16 nTmp = static_cast<sal_Int16>(elem.first.getLength());
495 if (nTmp > nMaxLeftCharCount)
496 nMaxLeftCharCount = nTmp;
499 nMaxRightCharCount = 0;
500 if (pFromRight)
502 for (auto const& elem : *pFromRight)
504 sal_Int16 nTmp = static_cast<sal_Int16>(elem.first.getLength());
505 if (nTmp > nMaxRightCharCount)
506 nMaxRightCharCount = nTmp;
510 bMaxCharCountIsValid = true;
512 sal_Int16 nRes = eDirection == ConversionDirection_FROM_LEFT ?
513 nMaxLeftCharCount : nMaxRightCharCount;
514 DBG_ASSERT( nRes >= 0, "invalid MaxCharCount" );
515 return nRes;
519 void SAL_CALL ConvDic::setPropertyType(
520 const OUString& rLeftText,
521 const OUString& rRightText,
522 sal_Int16 nPropertyType )
524 bool bHasElement = HasEntry( rLeftText, rRightText);
525 if (!bHasElement)
526 throw container::NoSuchElementException();
528 // currently we assume that entries with the same left text have the
529 // same PropertyType even if the right text is different...
530 if (pConvPropType)
531 pConvPropType->emplace( rLeftText, nPropertyType );
532 bIsModified = true;
536 sal_Int16 SAL_CALL ConvDic::getPropertyType(
537 const OUString& rLeftText,
538 const OUString& rRightText )
540 bool bHasElement = HasEntry( rLeftText, rRightText);
541 if (!bHasElement)
542 throw container::NoSuchElementException();
544 sal_Int16 nRes = ConversionPropertyType::NOT_DEFINED;
545 if (pConvPropType)
547 // still assuming that entries with same left text have same PropertyType
548 // even if they have different right text...
549 PropTypeMap::iterator aIt = pConvPropType->find( rLeftText );
550 if (aIt != pConvPropType->end())
551 nRes = (*aIt).second;
553 return nRes;
557 void SAL_CALL ConvDic::flush( )
559 MutexGuard aGuard( GetLinguMutex() );
561 if (!bIsModified)
562 return;
564 Save();
566 // notify listeners
567 EventObject aEvtObj;
568 aEvtObj.Source = uno::Reference< XFlushable >( this );
569 aFlushListeners.notifyEach( &util::XFlushListener::flushed, aEvtObj );
573 void SAL_CALL ConvDic::addFlushListener(
574 const uno::Reference< util::XFlushListener >& rxListener )
576 MutexGuard aGuard( GetLinguMutex() );
577 if (rxListener.is())
578 aFlushListeners.addInterface( rxListener );
582 void SAL_CALL ConvDic::removeFlushListener(
583 const uno::Reference< util::XFlushListener >& rxListener )
585 MutexGuard aGuard( GetLinguMutex() );
586 if (rxListener.is())
587 aFlushListeners.removeInterface( rxListener );
591 OUString SAL_CALL ConvDic::getImplementationName( )
593 return "com.sun.star.lingu2.ConvDic";
596 sal_Bool SAL_CALL ConvDic::supportsService( const OUString& rServiceName )
598 return cppu::supportsService(this, rServiceName);
601 uno::Sequence< OUString > SAL_CALL ConvDic::getSupportedServiceNames( )
603 return { SN_CONV_DICTIONARY };
607 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */