bump product version to 4.1.6.2
[LibreOffice.git] / linguistic / source / convdic.cxx
blobc21df30b58791403adace847cdf810feee720b72
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 <osl/mutex.hxx>
24 #include <tools/debug.hxx>
25 #include <tools/stream.hxx>
26 #include <tools/string.hxx>
27 #include <tools/urlobj.hxx>
28 #include <ucbhelper/content.hxx>
29 #include <comphelper/processfactory.hxx>
30 #include <unotools/streamwrap.hxx>
31 #include <unotools/ucbstreamhelper.hxx>
33 #include <com/sun/star/linguistic2/XConversionDictionary.hpp>
34 #include <com/sun/star/linguistic2/ConversionDictionaryType.hpp>
35 #include <com/sun/star/linguistic2/XConversionPropertyType.hpp>
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/XActiveDataSource.hpp>
45 #include <com/sun/star/io/XInputStream.hpp>
46 #include <com/sun/star/io/XOutputStream.hpp>
47 #include <com/sun/star/xml/sax/Writer.hpp>
48 #include <com/sun/star/document/XFilter.hpp>
49 #include <com/sun/star/beans/PropertyValue.hpp>
50 #include <com/sun/star/xml/sax/InputSource.hpp>
51 #include <com/sun/star/xml/sax/Parser.hpp>
54 #include "convdic.hxx"
55 #include "convdicxml.hxx"
56 #include "linguistic/misc.hxx"
57 #include "defs.hxx"
59 using namespace std;
60 using namespace utl;
61 using namespace osl;
62 using namespace com::sun::star;
63 using namespace com::sun::star::lang;
64 using namespace com::sun::star::uno;
65 using namespace com::sun::star::linguistic2;
66 using namespace linguistic;
69 #define SN_CONV_DICTIONARY "com.sun.star.linguistic2.ConversionDictionary"
71 void ReadThroughDic( const String &rMainURL, ConvDicXMLImport &rImport )
73 if (rMainURL.Len() == 0)
74 return;
75 DBG_ASSERT(!INetURLObject( rMainURL ).HasError(), "invalid URL");
77 uno::Reference< uno::XComponentContext > xContext( comphelper::getProcessComponentContext() );
79 // get xInputStream stream
80 uno::Reference< io::XInputStream > xIn;
81 try
83 uno::Reference< ucb::XSimpleFileAccess3 > xAccess( ucb::SimpleFileAccess::create(xContext) );
84 xIn = xAccess->openFileRead( rMainURL );
86 catch (const uno::Exception &)
88 DBG_ASSERT( 0, "failed to get input stream" );
90 if (!xIn.is())
91 return;
93 SvStreamPtr pStream = SvStreamPtr( utl::UcbStreamHelper::CreateStream( xIn ) );
95 // prepare ParserInputSource
96 xml::sax::InputSource aParserInput;
97 aParserInput.aInputStream = xIn;
99 // get parser
100 uno::Reference< xml::sax::XParser > xParser = xml::sax::Parser::create( xContext );
102 //!! keep a reference until everything is done to
103 //!! ensure the proper lifetime of the object
104 uno::Reference < xml::sax::XDocumentHandler > xFilter(
105 (xml::sax::XExtendedDocumentHandler *) &rImport, UNO_QUERY );
107 // connect parser and filter
108 xParser->setDocumentHandler( xFilter );
110 // finally, parser the stream
113 xParser->parseStream( aParserInput ); // implicitly calls ConvDicXMLImport::CreateContext
115 catch( xml::sax::SAXParseException& )
118 catch( xml::sax::SAXException& )
121 catch( io::IOException& )
126 sal_Bool IsConvDic( const String &rFileURL, sal_Int16 &nLang, sal_Int16 &nConvType )
128 sal_Bool bRes = sal_False;
130 if (rFileURL.Len() == 0)
131 return bRes;
133 // check if file extension matches CONV_DIC_EXT
134 String aExt;
135 xub_StrLen nPos = rFileURL.SearchBackward( '.' );
136 if (STRING_NOTFOUND != nPos)
137 aExt = rFileURL.Copy( nPos + 1 );
138 aExt.ToLowerAscii();
139 if (!aExt.EqualsAscii( CONV_DIC_EXT ))
140 return bRes;
142 // first argument being 0 should stop the file from being parsed
143 // up to the end (reading all entries) when the required
144 // data (language, conversion type) is found.
145 ConvDicXMLImport *pImport = new ConvDicXMLImport( 0, rFileURL );
147 //!! keep a first reference to ensure the lifetime of the object !!
148 uno::Reference< XInterface > xRef( (document::XFilter *) pImport, UNO_QUERY );
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;
166 ConvDic::ConvDic(
167 const String &rName,
168 sal_Int16 nLang,
169 sal_Int16 nConvType,
170 sal_Bool bBiDirectional,
171 const String &rMainURL) :
172 aFlushListeners( GetLinguMutex() )
174 aName = rName;
175 nLanguage = nLang;
176 nConversionType = nConvType;
177 aMainURL = rMainURL;
179 if (bBiDirectional)
180 pFromRight = std::auto_ptr< ConvMap >( new ConvMap );
181 if (nLang == LANGUAGE_CHINESE_SIMPLIFIED || nLang == LANGUAGE_CHINESE_TRADITIONAL)
182 pConvPropType = std::auto_ptr< PropTypeMap >( new PropTypeMap );
184 nMaxLeftCharCount = nMaxRightCharCount = 0;
185 bMaxCharCountIsValid = sal_True;
187 bNeedEntries = sal_True;
188 bIsModified = bIsActive = sal_False;
189 bIsReadonly = sal_False;
191 if( rMainURL.Len() > 0 )
193 sal_Bool bExists = sal_False;
194 bIsReadonly = IsReadOnly( rMainURL, &bExists );
196 if( !bExists ) // new empty dictionary
198 bNeedEntries = sal_False;
199 //! create physical representation of an **empty** dictionary
200 //! that could be found by the dictionary-list implementation
201 // (Note: empty dictionaries are not just empty files!)
202 Save();
203 bIsReadonly = IsReadOnly( rMainURL ); // will be sal_False if Save was successful
206 else
208 bNeedEntries = sal_False;
213 ConvDic::~ConvDic()
218 void ConvDic::Load()
220 DBG_ASSERT( !bIsModified, "dictionary is modified. Really do 'Load'?" );
222 //!! prevent function from being called recursively via HasEntry, AddEntry
223 bNeedEntries = sal_False;
224 ConvDicXMLImport *pImport = new ConvDicXMLImport( this, aMainURL );
225 //!! keep a first reference to ensure the lifetime of the object !!
226 uno::Reference< XInterface > xRef( (document::XFilter *) pImport, UNO_QUERY );
227 ReadThroughDic( aMainURL, *pImport ); // will implicitly add the entries
228 bIsModified = sal_False;
232 void ConvDic::Save()
234 DBG_ASSERT( !bNeedEntries, "saving while entries missing" );
235 if (aMainURL.Len() == 0 || bNeedEntries)
236 return;
237 DBG_ASSERT(!INetURLObject( aMainURL ).HasError(), "invalid URL");
239 uno::Reference< uno::XComponentContext > xContext( comphelper::getProcessComponentContext() );
241 // get XOutputStream stream
242 uno::Reference< io::XStream > xStream;
245 uno::Reference< ucb::XSimpleFileAccess3 > xAccess( ucb::SimpleFileAccess::create(xContext) );
246 xStream = xAccess->openFileReadWrite( aMainURL );
248 catch (const uno::Exception &)
250 DBG_ASSERT( 0, "failed to get input stream" );
252 if (!xStream.is())
253 return;
255 SvStreamPtr pStream = SvStreamPtr( utl::UcbStreamHelper::CreateStream( xStream ) );
257 // get XML writer
258 uno::Reference< xml::sax::XWriter > xSaxWriter = xml::sax::Writer::create(xContext);
260 if (xStream.is())
262 // connect XML writer to output stream
263 xSaxWriter->setOutputStream( xStream->getOutputStream() );
265 // prepare arguments (prepend doc handler to given arguments)
266 uno::Reference< xml::sax::XDocumentHandler > xDocHandler( xSaxWriter, UNO_QUERY );
267 ConvDicXMLExport *pExport = new ConvDicXMLExport( *this, aMainURL, xDocHandler );
268 //!! keep a first(!) reference until everything is done to
269 //!! ensure the proper lifetime of the object
270 uno::Reference< document::XFilter > aRef( (document::XFilter *) pExport );
271 sal_Bool bRet = pExport->Export(); // write entries to file
272 DBG_ASSERT( !pStream->GetError(), "I/O error while writing to stream" );
273 if (bRet)
274 bIsModified = sal_False;
276 DBG_ASSERT( !bIsModified, "dictionary still modified after save. Save failed?" );
280 ConvMap::iterator ConvDic::GetEntry( ConvMap &rMap, const OUString &rFirstText, const OUString &rSecondText )
282 pair< ConvMap::iterator, ConvMap::iterator > aRange =
283 rMap.equal_range( rFirstText );
284 ConvMap::iterator aPos = rMap.end();
285 for (ConvMap::iterator aIt = aRange.first;
286 aIt != aRange.second && aPos == rMap.end();
287 ++aIt)
289 if ((*aIt).second == rSecondText)
290 aPos = aIt;
292 return aPos;
296 sal_Bool ConvDic::HasEntry( const OUString &rLeftText, const OUString &rRightText )
298 if (bNeedEntries)
299 Load();
300 ConvMap::iterator aIt = GetEntry( aFromLeft, rLeftText, rRightText );
301 return aIt != aFromLeft.end();
305 void ConvDic::AddEntry( const OUString &rLeftText, const OUString &rRightText )
307 if (bNeedEntries)
308 Load();
310 DBG_ASSERT(!HasEntry( rLeftText, rRightText), "entry already exists" );
311 aFromLeft .insert( ConvMap::value_type( rLeftText, rRightText ) );
312 if (pFromRight.get())
313 pFromRight->insert( ConvMap::value_type( rRightText, rLeftText ) );
315 if (bMaxCharCountIsValid)
317 if (rLeftText.getLength() > nMaxLeftCharCount)
318 nMaxLeftCharCount = (sal_Int16) rLeftText.getLength();
319 if (pFromRight.get() && rRightText.getLength() > nMaxRightCharCount)
320 nMaxRightCharCount = (sal_Int16) rRightText.getLength();
323 bIsModified = sal_True;
327 void ConvDic::RemoveEntry( const OUString &rLeftText, const OUString &rRightText )
329 if (bNeedEntries)
330 Load();
332 ConvMap::iterator aLeftIt = GetEntry( aFromLeft, rLeftText, rRightText );
333 DBG_ASSERT( aLeftIt != aFromLeft.end(), "left map entry missing" );
334 aFromLeft .erase( aLeftIt );
336 if (pFromRight.get())
338 ConvMap::iterator aRightIt = GetEntry( *pFromRight, rRightText, rLeftText );
339 DBG_ASSERT( aRightIt != pFromRight->end(), "right map entry missing" );
340 pFromRight->erase( aRightIt );
343 bIsModified = sal_True;
344 bMaxCharCountIsValid = sal_False;
348 OUString SAL_CALL ConvDic::getName( )
349 throw (RuntimeException)
351 MutexGuard aGuard( GetLinguMutex() );
352 return aName;
356 Locale SAL_CALL ConvDic::getLocale( )
357 throw (RuntimeException)
359 MutexGuard aGuard( GetLinguMutex() );
360 return LanguageTag( nLanguage ).getLocale();
364 sal_Int16 SAL_CALL ConvDic::getConversionType( )
365 throw (RuntimeException)
367 MutexGuard aGuard( GetLinguMutex() );
368 return nConversionType;
372 void SAL_CALL ConvDic::setActive( sal_Bool bActivate )
373 throw (RuntimeException)
375 MutexGuard aGuard( GetLinguMutex() );
376 bIsActive = bActivate;
380 sal_Bool SAL_CALL ConvDic::isActive( )
381 throw (RuntimeException)
383 MutexGuard aGuard( GetLinguMutex() );
384 return bIsActive;
388 void SAL_CALL ConvDic::clear( )
389 throw (RuntimeException)
391 MutexGuard aGuard( GetLinguMutex() );
392 aFromLeft .clear();
393 if (pFromRight.get())
394 pFromRight->clear();
395 bNeedEntries = sal_False;
396 bIsModified = sal_True;
397 nMaxLeftCharCount = 0;
398 nMaxRightCharCount = 0;
399 bMaxCharCountIsValid = sal_True;
403 uno::Sequence< OUString > SAL_CALL ConvDic::getConversions(
404 const OUString& aText,
405 sal_Int32 nStartPos,
406 sal_Int32 nLength,
407 ConversionDirection eDirection,
408 sal_Int32 /*nTextConversionOptions*/ )
409 throw (IllegalArgumentException, RuntimeException)
411 MutexGuard aGuard( GetLinguMutex() );
413 if (!pFromRight.get() && eDirection == ConversionDirection_FROM_RIGHT)
414 return uno::Sequence< OUString >();
416 if (bNeedEntries)
417 Load();
419 OUString aLookUpText( aText.copy(nStartPos, nLength) );
420 ConvMap &rConvMap = eDirection == ConversionDirection_FROM_LEFT ?
421 aFromLeft : *pFromRight;
422 pair< ConvMap::iterator, ConvMap::iterator > aRange =
423 rConvMap.equal_range( aLookUpText );
425 sal_Int32 nCount = 0;
426 ConvMap::iterator aIt;
427 for (aIt = aRange.first; aIt != aRange.second; ++aIt)
428 ++nCount;
430 uno::Sequence< OUString > aRes( nCount );
431 OUString *pRes = aRes.getArray();
432 sal_Int32 i = 0;
433 for (aIt = aRange.first; aIt != aRange.second; ++aIt)
434 pRes[i++] = (*aIt).second;
436 return aRes;
440 static sal_Bool lcl_SeqHasEntry(
441 const OUString *pSeqStart, // first element to check
442 sal_Int32 nToCheck, // number of elements to check
443 const OUString &rText)
445 sal_Bool bRes = sal_False;
446 if (pSeqStart && nToCheck > 0)
448 const OUString *pDone = pSeqStart + nToCheck; // one behind last to check
449 while (!bRes && pSeqStart != pDone)
451 if (*pSeqStart++ == rText)
452 bRes = sal_True;
455 return bRes;
458 uno::Sequence< OUString > SAL_CALL ConvDic::getConversionEntries(
459 ConversionDirection eDirection )
460 throw (RuntimeException)
462 MutexGuard aGuard( GetLinguMutex() );
464 if (!pFromRight.get() && eDirection == ConversionDirection_FROM_RIGHT)
465 return uno::Sequence< OUString >();
467 if (bNeedEntries)
468 Load();
470 ConvMap &rConvMap = eDirection == ConversionDirection_FROM_LEFT ?
471 aFromLeft : *pFromRight;
472 uno::Sequence< OUString > aRes( rConvMap.size() );
473 OUString *pRes = aRes.getArray();
474 ConvMap::iterator aIt = rConvMap.begin();
475 sal_Int32 nIdx = 0;
476 while (aIt != rConvMap.end())
478 OUString aCurEntry( (*aIt).first );
479 // skip duplicate entries ( duplicate = duplicate entries
480 // respective to the evaluated side (FROM_LEFT or FROM_RIGHT).
481 // Thus if FROM_LEFT is evaluated for pairs (A,B) and (A,C)
482 // only one entry for A will be returned in the result)
483 if (nIdx == 0 || !lcl_SeqHasEntry( pRes, nIdx, aCurEntry ))
484 pRes[ nIdx++ ] = aCurEntry;
485 ++aIt;
487 aRes.realloc( nIdx );
489 return aRes;
493 void SAL_CALL ConvDic::addEntry(
494 const OUString& aLeftText,
495 const OUString& aRightText )
496 throw (IllegalArgumentException, container::ElementExistException, RuntimeException)
498 MutexGuard aGuard( GetLinguMutex() );
499 if (bNeedEntries)
500 Load();
501 if (HasEntry( aLeftText, aRightText ))
502 throw container::ElementExistException();
503 AddEntry( aLeftText, aRightText );
507 void SAL_CALL ConvDic::removeEntry(
508 const OUString& aLeftText,
509 const OUString& aRightText )
510 throw (container::NoSuchElementException, RuntimeException)
512 MutexGuard aGuard( GetLinguMutex() );
513 if (bNeedEntries)
514 Load();
515 if (!HasEntry( aLeftText, aRightText ))
516 throw container::NoSuchElementException();
517 RemoveEntry( aLeftText, aRightText );
521 sal_Int16 SAL_CALL ConvDic::getMaxCharCount( ConversionDirection eDirection )
522 throw (RuntimeException)
524 MutexGuard aGuard( GetLinguMutex() );
526 if (!pFromRight.get() && eDirection == ConversionDirection_FROM_RIGHT)
528 DBG_ASSERT( nMaxRightCharCount == 0, "max right char count should be 0" );
529 return 0;
532 if (bNeedEntries)
533 Load();
535 if (!bMaxCharCountIsValid)
537 nMaxLeftCharCount = 0;
538 ConvMap::iterator aIt = aFromLeft.begin();
539 while (aIt != aFromLeft.end())
541 sal_Int16 nTmp = (sal_Int16) (*aIt).first.getLength();
542 if (nTmp > nMaxLeftCharCount)
543 nMaxLeftCharCount = nTmp;
544 ++aIt;
547 nMaxRightCharCount = 0;
548 if (pFromRight.get())
550 aIt = pFromRight->begin();
551 while (aIt != pFromRight->end())
553 sal_Int16 nTmp = (sal_Int16) (*aIt).first.getLength();
554 if (nTmp > nMaxRightCharCount)
555 nMaxRightCharCount = nTmp;
556 ++aIt;
560 bMaxCharCountIsValid = sal_True;
562 sal_Int16 nRes = eDirection == ConversionDirection_FROM_LEFT ?
563 nMaxLeftCharCount : nMaxRightCharCount;
564 DBG_ASSERT( nRes >= 0, "invalid MaxCharCount" );
565 return nRes;
569 void SAL_CALL ConvDic::setPropertyType(
570 const OUString& rLeftText,
571 const OUString& rRightText,
572 sal_Int16 nPropertyType )
573 throw (container::NoSuchElementException, IllegalArgumentException, RuntimeException)
575 sal_Bool bHasElement = HasEntry( rLeftText, rRightText);
576 if (!bHasElement)
577 throw container::NoSuchElementException();
579 // currently we assume that entries with the same left text have the
580 // same PropertyType even if the right text is different...
581 if (pConvPropType.get())
582 pConvPropType->insert( PropTypeMap::value_type( rLeftText, nPropertyType ) );
583 bIsModified = sal_True;
587 sal_Int16 SAL_CALL ConvDic::getPropertyType(
588 const OUString& rLeftText,
589 const OUString& rRightText )
590 throw (container::NoSuchElementException, RuntimeException)
592 sal_Bool bHasElement = HasEntry( rLeftText, rRightText);
593 if (!bHasElement)
594 throw container::NoSuchElementException();
596 sal_Int16 nRes = ConversionPropertyType::NOT_DEFINED;
597 if (pConvPropType.get())
599 // still assuming that entries with same left text have same PropertyType
600 // even if they have different right text...
601 PropTypeMap::iterator aIt = pConvPropType->find( rLeftText );
602 if (aIt != pConvPropType->end())
603 nRes = (*aIt).second;
605 return nRes;
609 void SAL_CALL ConvDic::flush( )
610 throw (RuntimeException)
612 MutexGuard aGuard( GetLinguMutex() );
614 if (!bIsModified)
615 return;
617 Save();
619 // notify listeners
620 EventObject aEvtObj;
621 aEvtObj.Source = uno::Reference< XFlushable >( this );
622 cppu::OInterfaceIteratorHelper aIt( aFlushListeners );
623 while (aIt.hasMoreElements())
625 uno::Reference< util::XFlushListener > xRef( aIt.next(), UNO_QUERY );
626 if (xRef.is())
627 xRef->flushed( aEvtObj );
632 void SAL_CALL ConvDic::addFlushListener(
633 const uno::Reference< util::XFlushListener >& rxListener )
634 throw (RuntimeException)
636 MutexGuard aGuard( GetLinguMutex() );
637 if (rxListener.is())
638 aFlushListeners.addInterface( rxListener );
642 void SAL_CALL ConvDic::removeFlushListener(
643 const uno::Reference< util::XFlushListener >& rxListener )
644 throw (RuntimeException)
646 MutexGuard aGuard( GetLinguMutex() );
647 if (rxListener.is())
648 aFlushListeners.removeInterface( rxListener );
652 OUString SAL_CALL ConvDic::getImplementationName( )
653 throw (RuntimeException)
655 MutexGuard aGuard( GetLinguMutex() );
656 return getImplementationName_Static();
660 sal_Bool SAL_CALL ConvDic::supportsService( const OUString& rServiceName )
661 throw (RuntimeException)
663 MutexGuard aGuard( GetLinguMutex() );
664 sal_Bool bRes = sal_False;
665 if ( rServiceName == SN_CONV_DICTIONARY )
666 bRes = sal_True;
667 return bRes;
671 uno::Sequence< OUString > SAL_CALL ConvDic::getSupportedServiceNames( )
672 throw (RuntimeException)
674 MutexGuard aGuard( GetLinguMutex() );
675 return getSupportedServiceNames_Static();
679 uno::Sequence< OUString > ConvDic::getSupportedServiceNames_Static()
680 throw()
682 uno::Sequence< OUString > aSNS( 1 );
683 aSNS.getArray()[0] = SN_CONV_DICTIONARY ;
684 return aSNS;
689 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */