1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: convdic.cxx,v $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_linguistic.hxx"
33 #include <i18npool/lang.h>
34 #include <tools/urlobj.hxx>
35 #include <tools/debug.hxx>
36 #include <tools/fsys.hxx>
37 #include <tools/stream.hxx>
38 #include <tools/string.hxx>
39 #include <tools/stream.hxx>
40 #include <sfx2/docfile.hxx>
41 #include <osl/mutex.hxx>
42 #include <unotools/processfactory.hxx>
43 #include <ucbhelper/content.hxx>
45 #include <cppuhelper/factory.hxx> // helper for factories
46 #include <com/sun/star/linguistic2/XConversionDictionary.hpp>
47 #include <com/sun/star/linguistic2/ConversionDictionaryType.hpp>
48 #include <com/sun/star/linguistic2/XConversionPropertyType.hpp>
49 #include <com/sun/star/linguistic2/ConversionPropertyType.hpp>
50 #include <com/sun/star/util/XFlushable.hpp>
51 #include <com/sun/star/lang/Locale.hpp>
52 #include <com/sun/star/lang/EventObject.hpp>
53 #ifndef _COM_SUN_STAR_UNO_REFERENCE_HPP_
54 #include <com/sun/star/uno/Reference.h>
56 #include <com/sun/star/registry/XRegistryKey.hpp>
57 #include <com/sun/star/util/XFlushListener.hpp>
58 #include <com/sun/star/io/XActiveDataSource.hpp>
59 #include <com/sun/star/xml/sax/XDocumentHandler.hpp>
60 #include <com/sun/star/document/XFilter.hpp>
61 #include <com/sun/star/beans/PropertyValue.hpp>
62 #include <com/sun/star/xml/sax/InputSource.hpp>
63 #include <com/sun/star/xml/sax/XParser.hpp>
64 #include <unotools/streamwrap.hxx>
67 #include "convdic.hxx"
68 #include "convdicxml.hxx"
76 using namespace com::sun::star
;
77 using namespace com::sun::star::lang
;
78 using namespace com::sun::star::uno
;
79 using namespace com::sun::star::linguistic2
;
80 using namespace linguistic
;
82 #define SN_CONV_DICTIONARY "com.sun.star.linguistic2.ConversionDictionary"
83 #define SN_HCD_CONV_DICTIONARY "com.sun.star.linguistic2.HangulHanjaConversionDictionary"
86 ///////////////////////////////////////////////////////////////////////////
88 void ReadThroughDic( const String
&rMainURL
, ConvDicXMLImport
&rImport
)
90 if (rMainURL
.Len() == 0)
93 // get stream to be used
94 DBG_ASSERT(!INetURLObject( rMainURL
).HasError(), "invalid URL");
95 SfxMedium
aMedium( rMainURL
, STREAM_READ
| STREAM_SHARE_DENYWRITE
, FALSE
);
96 SvStream
*pStream
= aMedium
.GetInStream();
97 DBG_ASSERT( pStream
, "input stream missing" );
98 if (!pStream
|| pStream
->GetError())
101 uno::Reference
< lang::XMultiServiceFactory
> xServiceFactory(
102 utl::getProcessServiceFactory() );
103 DBG_ASSERT( xServiceFactory
.is(), "XMLReader::Read: got no service manager" );
104 if (!xServiceFactory
.is())
107 uno::Reference
< io::XInputStream
> xIn
= new utl::OInputStreamWrapper( *pStream
);
108 DBG_ASSERT( xIn
.is(), "input stream missing" );
110 ULONG nError
= sal::static_int_cast
< ULONG
>(-1);
112 // prepare ParserInputSource
113 xml::sax::InputSource aParserInput
;
114 aParserInput
.aInputStream
= xIn
;
117 uno::Reference
< xml::sax::XParser
> xParser
;
120 xParser
= uno::Reference
< xml::sax::XParser
>( xServiceFactory
->createInstance(
121 A2OU( "com.sun.star.xml.sax.Parser" ) ), UNO_QUERY
);
123 catch (uno::Exception
&)
126 DBG_ASSERT( xParser
.is(), "Can't create parser" );
131 //ConvDicXMLImport *pImport = new ConvDicXMLImport( this, rMainURL );
132 //!! keep a reference until everything is done to
133 //!! ensure the proper lifetime of the object
134 uno::Reference
< xml::sax::XDocumentHandler
> xFilter(
135 (xml::sax::XExtendedDocumentHandler
*) &rImport
, UNO_QUERY
);
137 // connect parser and filter
138 xParser
->setDocumentHandler( xFilter
);
140 // finally, parser the stream
143 xParser
->parseStream( aParserInput
); // implicitly calls ConvDicXMLImport::CreateContext
144 if (rImport
.GetSuccess())
147 catch( xml::sax::SAXParseException
& )
150 // nError = ERRCODE_SFX_WRONGPASSWORD;
152 catch( xml::sax::SAXException
& )
155 // nError = ERRCODE_SFX_WRONGPASSWORD;
157 catch( io::IOException
& )
163 BOOL
IsConvDic( const String
&rFileURL
, INT16
&nLang
, sal_Int16
&nConvType
)
167 if (rFileURL
.Len() == 0)
170 // check if file extension matches CONV_DIC_EXT
172 xub_StrLen nPos
= rFileURL
.SearchBackward( '.' );
173 if (STRING_NOTFOUND
!= nPos
)
174 aExt
= rFileURL
.Copy( nPos
+ 1 );
176 if (!aExt
.EqualsAscii( CONV_DIC_EXT
))
179 // first argument being 0 should stop the file from being parsed
180 // up to the end (reading all entries) when the required
181 // data (language, conversion type) is found.
182 ConvDicXMLImport
*pImport
= new ConvDicXMLImport( 0, rFileURL
);
184 //!! keep a first reference to ensure the lifetime of the object !!
185 uno::Reference
< XInterface
> xRef( (document::XFilter
*) pImport
, UNO_QUERY
);
187 ReadThroughDic( rFileURL
, *pImport
); // will implicitly add the entries
188 bRes
= pImport
->GetLanguage() != LANGUAGE_NONE
&&
189 pImport
->GetConversionType() != -1;
190 DBG_ASSERT( bRes
, "conversion dictionary corrupted?" );
194 nLang
= pImport
->GetLanguage();
195 nConvType
= pImport
->GetConversionType();
202 ///////////////////////////////////////////////////////////////////////////
209 const String
&rMainURL
) :
210 aFlushListeners( GetLinguMutex() )
214 nConversionType
= nConvType
;
218 pFromRight
= std::auto_ptr
< ConvMap
>( new ConvMap
);
219 if (nLang
== LANGUAGE_CHINESE_SIMPLIFIED
|| nLang
== LANGUAGE_CHINESE_TRADITIONAL
)
220 pConvPropType
= std::auto_ptr
< PropTypeMap
>( new PropTypeMap
);
222 nMaxLeftCharCount
= nMaxRightCharCount
= 0;
223 bMaxCharCountIsValid
= TRUE
;
226 bIsModified
= bIsActive
= FALSE
;
229 if( rMainURL
.Len() > 0 )
231 BOOL bExists
= FALSE
;
232 bIsReadonly
= IsReadOnly( rMainURL
, &bExists
);
234 if( !bExists
) // new empty dictionary
236 bNeedEntries
= FALSE
;
237 //! create physical representation of an **empty** dictionary
238 //! that could be found by the dictionary-list implementation
239 // (Note: empty dictionaries are not just empty files!)
241 bIsReadonly
= IsReadOnly( rMainURL
); // will be FALSE if Save was succesfull
246 bNeedEntries
= FALSE
;
258 DBG_ASSERT( !bIsModified
, "dictionary is modified. Really do 'Load'?" );
260 //!! prevent function from being called recursively via HasEntry, AddEntry
261 bNeedEntries
= FALSE
;
263 ConvDicXMLImport
*pImport
= new ConvDicXMLImport( this, aMainURL
);
264 //!! keep a first reference to ensure the lifetime of the object !!
265 uno::Reference
< XInterface
> xRef( (document::XFilter
*) pImport
, UNO_QUERY
);
266 ReadThroughDic( aMainURL
, *pImport
); // will implicitly add the entries
274 DBG_ASSERT( !bNeedEntries
, "saving while entries missing" );
275 if (aMainURL
.Len() == 0 || bNeedEntries
)
278 DBG_ASSERT(!INetURLObject( aMainURL
).HasError(), "invalid URL");
279 SfxMedium
aMedium( aMainURL
, STREAM_WRITE
| STREAM_TRUNC
| STREAM_SHARE_DENYALL
,
281 aMedium
.CreateTempFile(); // use temp file to write to...
283 SvStream
*pStream
= aMedium
.GetOutStream();
284 DBG_ASSERT( pStream
, "output stream missing" );
285 if (!pStream
|| pStream
->GetError())
287 uno::Reference
< io::XOutputStream
> xOut(
288 new utl::OOutputStreamWrapper( *pStream
) );
289 DBG_ASSERT( xOut
.is(), "output stream missing" );
292 uno::Reference
< lang::XMultiServiceFactory
> xServiceFactory(
293 utl::getProcessServiceFactory() );
294 uno::Reference
< io::XActiveDataSource
> xSaxWriter
;
295 if (xServiceFactory
.is())
299 xSaxWriter
= uno::Reference
< io::XActiveDataSource
>(
300 xServiceFactory
->createInstance(
301 OUString::createFromAscii( "com.sun.star.xml.sax.Writer" ) ), UNO_QUERY
);
303 catch (uno::Exception
&)
307 DBG_ASSERT( xSaxWriter
.is(), "can't instantiate XML writer" );
309 if (xSaxWriter
.is() && xOut
.is())
311 // connect XML writer to output stream
312 xSaxWriter
->setOutputStream( xOut
);
314 // prepare arguments (prepend doc handler to given arguments)
315 uno::Reference
< xml::sax::XDocumentHandler
> xDocHandler( xSaxWriter
, UNO_QUERY
);
317 ConvDicXMLExport
*pExport
= new ConvDicXMLExport( *this, aMainURL
, xDocHandler
);
318 //!! keep a first(!) reference until everything is done to
319 //!! ensure the proper lifetime of the object
320 uno::Reference
< document::XFilter
> aRef( (document::XFilter
*) pExport
);
321 sal_Bool bRet
= pExport
->Export( aMedium
); // write entries to file
322 DBG_ASSERT( !pStream
->GetError(), "I/O error while writing to stream" );
325 // flush file, close it and release any lock
331 DBG_ASSERT( !bIsModified
, "dictionary still modified after save. Save failed?" );
335 ConvMap::iterator
ConvDic::GetEntry( ConvMap
&rMap
, const rtl::OUString
&rFirstText
, const rtl::OUString
&rSecondText
)
337 pair
< ConvMap::iterator
, ConvMap::iterator
> aRange
=
338 rMap
.equal_range( rFirstText
);
339 ConvMap::iterator aPos
= rMap
.end();
340 for (ConvMap::iterator aIt
= aRange
.first
;
341 aIt
!= aRange
.second
&& aPos
== rMap
.end();
344 if ((*aIt
).second
== rSecondText
)
351 BOOL
ConvDic::HasEntry( const OUString
&rLeftText
, const OUString
&rRightText
)
355 ConvMap::iterator aIt
= GetEntry( aFromLeft
, rLeftText
, rRightText
);
356 return aIt
!= aFromLeft
.end();
360 void ConvDic::AddEntry( const OUString
&rLeftText
, const OUString
&rRightText
)
365 DBG_ASSERT(!HasEntry( rLeftText
, rRightText
), "entry already exists" );
366 aFromLeft
.insert( ConvMap::value_type( rLeftText
, rRightText
) );
367 if (pFromRight
.get())
368 pFromRight
->insert( ConvMap::value_type( rRightText
, rLeftText
) );
370 if (bMaxCharCountIsValid
)
372 if (rLeftText
.getLength() > nMaxLeftCharCount
)
373 nMaxLeftCharCount
= (sal_Int16
) rLeftText
.getLength();
374 if (pFromRight
.get() && rRightText
.getLength() > nMaxRightCharCount
)
375 nMaxRightCharCount
= (sal_Int16
) rRightText
.getLength();
382 void ConvDic::RemoveEntry( const OUString
&rLeftText
, const OUString
&rRightText
)
387 ConvMap::iterator aLeftIt
= GetEntry( aFromLeft
, rLeftText
, rRightText
);
388 DBG_ASSERT( aLeftIt
!= aFromLeft
.end(), "left map entry missing" );
389 aFromLeft
.erase( aLeftIt
);
391 if (pFromRight
.get())
393 ConvMap::iterator aRightIt
= GetEntry( *pFromRight
, rRightText
, rLeftText
);
394 DBG_ASSERT( aRightIt
!= pFromRight
->end(), "right map entry missing" );
395 pFromRight
->erase( aRightIt
);
399 bMaxCharCountIsValid
= FALSE
;
403 OUString SAL_CALL
ConvDic::getName( )
404 throw (RuntimeException
)
406 MutexGuard
aGuard( GetLinguMutex() );
411 Locale SAL_CALL
ConvDic::getLocale( )
412 throw (RuntimeException
)
414 MutexGuard
aGuard( GetLinguMutex() );
415 return CreateLocale( nLanguage
);
419 sal_Int16 SAL_CALL
ConvDic::getConversionType( )
420 throw (RuntimeException
)
422 MutexGuard
aGuard( GetLinguMutex() );
423 return nConversionType
;
427 void SAL_CALL
ConvDic::setActive( sal_Bool bActivate
)
428 throw (RuntimeException
)
430 MutexGuard
aGuard( GetLinguMutex() );
431 bIsActive
= bActivate
;
435 sal_Bool SAL_CALL
ConvDic::isActive( )
436 throw (RuntimeException
)
438 MutexGuard
aGuard( GetLinguMutex() );
443 void SAL_CALL
ConvDic::clear( )
444 throw (RuntimeException
)
446 MutexGuard
aGuard( GetLinguMutex() );
448 if (pFromRight
.get())
450 bNeedEntries
= FALSE
;
452 nMaxLeftCharCount
= 0;
453 nMaxRightCharCount
= 0;
454 bMaxCharCountIsValid
= TRUE
;
458 uno::Sequence
< OUString
> SAL_CALL
ConvDic::getConversions(
459 const OUString
& aText
,
462 ConversionDirection eDirection
,
463 sal_Int32
/*nTextConversionOptions*/ )
464 throw (IllegalArgumentException
, RuntimeException
)
466 MutexGuard
aGuard( GetLinguMutex() );
468 if (!pFromRight
.get() && eDirection
== ConversionDirection_FROM_RIGHT
)
469 return uno::Sequence
< OUString
>();
474 OUString
aLookUpText( aText
.copy(nStartPos
, nLength
) );
475 ConvMap
&rConvMap
= eDirection
== ConversionDirection_FROM_LEFT
?
476 aFromLeft
: *pFromRight
;
477 pair
< ConvMap::iterator
, ConvMap::iterator
> aRange
=
478 rConvMap
.equal_range( aLookUpText
);
481 ConvMap::iterator aIt
;
482 for (aIt
= aRange
.first
; aIt
!= aRange
.second
; ++aIt
)
485 uno::Sequence
< OUString
> aRes( nCount
);
486 OUString
*pRes
= aRes
.getArray();
488 for (aIt
= aRange
.first
; aIt
!= aRange
.second
; ++aIt
)
489 pRes
[i
++] = (*aIt
).second
;
495 static BOOL
lcl_SeqHasEntry(
496 const OUString
*pSeqStart
, // first element to check
497 INT32 nToCheck
, // number of elements to check
498 const OUString
&rText
)
501 if (pSeqStart
&& nToCheck
> 0)
503 const OUString
*pDone
= pSeqStart
+ nToCheck
; // one behind last to check
504 while (!bRes
&& pSeqStart
!= pDone
)
506 if (*pSeqStart
++ == rText
)
513 uno::Sequence
< OUString
> SAL_CALL
ConvDic::getConversionEntries(
514 ConversionDirection eDirection
)
515 throw (RuntimeException
)
517 MutexGuard
aGuard( GetLinguMutex() );
519 if (!pFromRight
.get() && eDirection
== ConversionDirection_FROM_RIGHT
)
520 return uno::Sequence
< OUString
>();
525 ConvMap
&rConvMap
= eDirection
== ConversionDirection_FROM_LEFT
?
526 aFromLeft
: *pFromRight
;
527 uno::Sequence
< OUString
> aRes( rConvMap
.size() );
528 OUString
*pRes
= aRes
.getArray();
529 ConvMap::iterator aIt
= rConvMap
.begin();
531 while (aIt
!= rConvMap
.end())
533 OUString
aCurEntry( (*aIt
).first
);
534 // skip duplicate entries ( duplicate = duplicate entries
535 // respective to the evaluated side (FROM_LEFT or FROM_RIGHT).
536 // Thus if FROM_LEFT is evaluated for pairs (A,B) and (A,C)
537 // only one entry for A will be returned in the result)
538 if (nIdx
== 0 || !lcl_SeqHasEntry( pRes
, nIdx
, aCurEntry
))
539 pRes
[ nIdx
++ ] = aCurEntry
;
542 aRes
.realloc( nIdx
);
548 void SAL_CALL
ConvDic::addEntry(
549 const OUString
& aLeftText
,
550 const OUString
& aRightText
)
551 throw (IllegalArgumentException
, container::ElementExistException
, RuntimeException
)
553 MutexGuard
aGuard( GetLinguMutex() );
556 if (HasEntry( aLeftText
, aRightText
))
557 throw container::ElementExistException();
558 AddEntry( aLeftText
, aRightText
);
562 void SAL_CALL
ConvDic::removeEntry(
563 const OUString
& aLeftText
,
564 const OUString
& aRightText
)
565 throw (container::NoSuchElementException
, RuntimeException
)
567 MutexGuard
aGuard( GetLinguMutex() );
570 if (!HasEntry( aLeftText
, aRightText
))
571 throw container::NoSuchElementException();
572 RemoveEntry( aLeftText
, aRightText
);
576 sal_Int16 SAL_CALL
ConvDic::getMaxCharCount( ConversionDirection eDirection
)
577 throw (RuntimeException
)
579 MutexGuard
aGuard( GetLinguMutex() );
581 if (!pFromRight
.get() && eDirection
== ConversionDirection_FROM_RIGHT
)
583 DBG_ASSERT( nMaxRightCharCount
== 0, "max right char count should be 0" );
590 if (!bMaxCharCountIsValid
)
592 nMaxLeftCharCount
= 0;
593 ConvMap::iterator aIt
= aFromLeft
.begin();
594 while (aIt
!= aFromLeft
.end())
596 sal_Int16 nTmp
= (sal_Int16
) (*aIt
).first
.getLength();
597 if (nTmp
> nMaxLeftCharCount
)
598 nMaxLeftCharCount
= nTmp
;
602 nMaxRightCharCount
= 0;
603 if (pFromRight
.get())
605 aIt
= pFromRight
->begin();
606 while (aIt
!= pFromRight
->end())
608 sal_Int16 nTmp
= (sal_Int16
) (*aIt
).first
.getLength();
609 if (nTmp
> nMaxRightCharCount
)
610 nMaxRightCharCount
= nTmp
;
615 bMaxCharCountIsValid
= TRUE
;
617 sal_Int16 nRes
= eDirection
== ConversionDirection_FROM_LEFT
?
618 nMaxLeftCharCount
: nMaxRightCharCount
;
619 DBG_ASSERT( nRes
>= 0, "invalid MaxCharCount" );
624 void SAL_CALL
ConvDic::setPropertyType(
625 const OUString
& rLeftText
,
626 const OUString
& rRightText
,
627 sal_Int16 nPropertyType
)
628 throw (container::NoSuchElementException
, IllegalArgumentException
, RuntimeException
)
630 sal_Bool bHasElement
= HasEntry( rLeftText
, rRightText
);
632 throw container::NoSuchElementException();
634 // currently we assume that entries with the same left text have the
635 // same PropertyType even if the right text is different...
636 if (pConvPropType
.get())
637 pConvPropType
->insert( PropTypeMap::value_type( rLeftText
, nPropertyType
) );
638 bIsModified
= sal_True
;
642 sal_Int16 SAL_CALL
ConvDic::getPropertyType(
643 const OUString
& rLeftText
,
644 const OUString
& rRightText
)
645 throw (container::NoSuchElementException
, RuntimeException
)
647 sal_Bool bHasElement
= HasEntry( rLeftText
, rRightText
);
649 throw container::NoSuchElementException();
651 sal_Int16 nRes
= ConversionPropertyType::NOT_DEFINED
;
652 if (pConvPropType
.get())
654 // still assuming that entries with same left text have same PropertyType
655 // even if they have different right text...
656 PropTypeMap::iterator aIt
= pConvPropType
->find( rLeftText
);
657 if (aIt
!= pConvPropType
->end())
658 nRes
= (*aIt
).second
;
664 void SAL_CALL
ConvDic::flush( )
665 throw (RuntimeException
)
667 MutexGuard
aGuard( GetLinguMutex() );
676 aEvtObj
.Source
= uno::Reference
< XFlushable
>( this );
677 cppu::OInterfaceIteratorHelper
aIt( aFlushListeners
);
678 while (aIt
.hasMoreElements())
680 uno::Reference
< util::XFlushListener
> xRef( aIt
.next(), UNO_QUERY
);
682 xRef
->flushed( aEvtObj
);
687 void SAL_CALL
ConvDic::addFlushListener(
688 const uno::Reference
< util::XFlushListener
>& rxListener
)
689 throw (RuntimeException
)
691 MutexGuard
aGuard( GetLinguMutex() );
693 aFlushListeners
.addInterface( rxListener
);
697 void SAL_CALL
ConvDic::removeFlushListener(
698 const uno::Reference
< util::XFlushListener
>& rxListener
)
699 throw (RuntimeException
)
701 MutexGuard
aGuard( GetLinguMutex() );
703 aFlushListeners
.removeInterface( rxListener
);
707 OUString SAL_CALL
ConvDic::getImplementationName( )
708 throw (RuntimeException
)
710 MutexGuard
aGuard( GetLinguMutex() );
711 return getImplementationName_Static();
715 sal_Bool SAL_CALL
ConvDic::supportsService( const OUString
& rServiceName
)
716 throw (RuntimeException
)
718 MutexGuard
aGuard( GetLinguMutex() );
719 sal_Bool bRes
= sal_False
;
720 if (rServiceName
.equalsAscii( SN_CONV_DICTIONARY
))
726 uno::Sequence
< OUString
> SAL_CALL
ConvDic::getSupportedServiceNames( )
727 throw (RuntimeException
)
729 MutexGuard
aGuard( GetLinguMutex() );
730 return getSupportedServiceNames_Static();
734 uno::Sequence
< OUString
> ConvDic::getSupportedServiceNames_Static()
737 uno::Sequence
< OUString
> aSNS( 1 );
738 aSNS
.getArray()[0] = A2OU( SN_CONV_DICTIONARY
);
742 ///////////////////////////////////////////////////////////////////////////