1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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"
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)
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
;
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" );
93 SvStreamPtr pStream
= SvStreamPtr( utl::UcbStreamHelper::CreateStream( xIn
) );
95 // prepare ParserInputSource
96 xml::sax::InputSource aParserInput
;
97 aParserInput
.aInputStream
= xIn
;
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)
133 // check if file extension matches CONV_DIC_EXT
135 xub_StrLen nPos
= rFileURL
.SearchBackward( '.' );
136 if (STRING_NOTFOUND
!= nPos
)
137 aExt
= rFileURL
.Copy( nPos
+ 1 );
139 if (!aExt
.EqualsAscii( CONV_DIC_EXT
))
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?" );
157 nLang
= pImport
->GetLanguage();
158 nConvType
= pImport
->GetConversionType();
170 sal_Bool bBiDirectional
,
171 const String
&rMainURL
) :
172 aFlushListeners( GetLinguMutex() )
176 nConversionType
= nConvType
;
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!)
203 bIsReadonly
= IsReadOnly( rMainURL
); // will be sal_False if Save was successful
208 bNeedEntries
= sal_False
;
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
;
234 DBG_ASSERT( !bNeedEntries
, "saving while entries missing" );
235 if (aMainURL
.Len() == 0 || bNeedEntries
)
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" );
255 SvStreamPtr pStream
= SvStreamPtr( utl::UcbStreamHelper::CreateStream( xStream
) );
258 uno::Reference
< xml::sax::XWriter
> xSaxWriter
= xml::sax::Writer::create(xContext
);
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" );
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();
289 if ((*aIt
).second
== rSecondText
)
296 sal_Bool
ConvDic::HasEntry( const OUString
&rLeftText
, const OUString
&rRightText
)
300 ConvMap::iterator aIt
= GetEntry( aFromLeft
, rLeftText
, rRightText
);
301 return aIt
!= aFromLeft
.end();
305 void ConvDic::AddEntry( const OUString
&rLeftText
, const OUString
&rRightText
)
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
)
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() );
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() );
388 void SAL_CALL
ConvDic::clear( )
389 throw (RuntimeException
)
391 MutexGuard
aGuard( GetLinguMutex() );
393 if (pFromRight
.get())
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
,
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
>();
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
)
430 uno::Sequence
< OUString
> aRes( nCount
);
431 OUString
*pRes
= aRes
.getArray();
433 for (aIt
= aRange
.first
; aIt
!= aRange
.second
; ++aIt
)
434 pRes
[i
++] = (*aIt
).second
;
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
)
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
>();
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();
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
;
487 aRes
.realloc( nIdx
);
493 void SAL_CALL
ConvDic::addEntry(
494 const OUString
& aLeftText
,
495 const OUString
& aRightText
)
496 throw (IllegalArgumentException
, container::ElementExistException
, RuntimeException
)
498 MutexGuard
aGuard( GetLinguMutex() );
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() );
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" );
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
;
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
;
560 bMaxCharCountIsValid
= sal_True
;
562 sal_Int16 nRes
= eDirection
== ConversionDirection_FROM_LEFT
?
563 nMaxLeftCharCount
: nMaxRightCharCount
;
564 DBG_ASSERT( nRes
>= 0, "invalid MaxCharCount" );
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
);
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
);
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
;
609 void SAL_CALL
ConvDic::flush( )
610 throw (RuntimeException
)
612 MutexGuard
aGuard( GetLinguMutex() );
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
);
627 xRef
->flushed( aEvtObj
);
632 void SAL_CALL
ConvDic::addFlushListener(
633 const uno::Reference
< util::XFlushListener
>& rxListener
)
634 throw (RuntimeException
)
636 MutexGuard
aGuard( GetLinguMutex() );
638 aFlushListeners
.addInterface( rxListener
);
642 void SAL_CALL
ConvDic::removeFlushListener(
643 const uno::Reference
< util::XFlushListener
>& rxListener
)
644 throw (RuntimeException
)
646 MutexGuard
aGuard( GetLinguMutex() );
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
)
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()
682 uno::Sequence
< OUString
> aSNS( 1 );
683 aSNS
.getArray()[0] = SN_CONV_DICTIONARY
;
689 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */