1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_linguistic.hxx"
30 #include <tools/urlobj.hxx>
31 #include <tools/debug.hxx>
32 #include <tools/fsys.hxx>
33 #include <tools/string.hxx>
34 #include <i18npool/mslangid.hxx>
35 #include <tools/stream.hxx>
36 #include <osl/mutex.hxx>
37 #include <unotools/processfactory.hxx>
38 #include <ucbhelper/content.hxx>
39 
40 #include <cppuhelper/factory.hxx>   // helper for factories
41 #include <com/sun/star/linguistic2/XConversionDictionary.hpp>
42 #include <com/sun/star/linguistic2/ConversionDictionaryType.hpp>
43 #include <com/sun/star/linguistic2/ConversionPropertyType.hpp>
44 #include <com/sun/star/util/XFlushable.hpp>
45 #include <com/sun/star/lang/Locale.hpp>
46 #include <com/sun/star/lang/EventObject.hpp>
47 #ifndef _COM_SUN_STAR_UNO_REFERENCE_HPP_
48 #include <com/sun/star/uno/Reference.h>
49 #endif
50 #include <com/sun/star/registry/XRegistryKey.hpp>
51 #include <com/sun/star/util/XFlushListener.hpp>
52 #include <com/sun/star/io/XActiveDataSource.hpp>
53 #include <com/sun/star/document/XFilter.hpp>
54 #include <com/sun/star/beans/PropertyValue.hpp>
55 #include <xmloff/nmspmap.hxx>
56 #include <xmloff/xmlnmspe.hxx>
57 #include <unotools/streamwrap.hxx>
58 
59 #include "convdic.hxx"
60 #include "convdicxml.hxx"
61 #include "linguistic/misc.hxx"
62 #include "defs.hxx"
63 
64 using namespace std;
65 using namespace utl;
66 using namespace osl;
67 using namespace rtl;
68 using namespace com::sun::star;
69 using namespace com::sun::star::lang;
70 using namespace com::sun::star::uno;
71 using namespace com::sun::star::linguistic2;
72 using namespace linguistic;
73 
74 #define XML_NAMESPACE_TCD_STRING        "http://openoffice.org/2003/text-conversion-dictionary"
75 #define CONV_TYPE_HANGUL_HANJA          "Hangul / Hanja"
76 #define CONV_TYPE_SCHINESE_TCHINESE     "Chinese simplified / Chinese traditional"
77 
78 ///////////////////////////////////////////////////////////////////////////
79 
80 static const OUString ConversionTypeToText( sal_Int16 nConversionType )
81 {
82     OUString aRes;
83     if (nConversionType == ConversionDictionaryType::HANGUL_HANJA)
84         aRes = A2OU( CONV_TYPE_HANGUL_HANJA );
85     else if (nConversionType == ConversionDictionaryType::SCHINESE_TCHINESE)
86         aRes = A2OU( CONV_TYPE_SCHINESE_TCHINESE );
87     return aRes;
88 }
89 
90 static sal_Int16 GetConversionTypeFromText( const String &rText )
91 {
92     sal_Int16 nRes = -1;
93     if (rText.EqualsAscii( CONV_TYPE_HANGUL_HANJA ))
94         nRes = ConversionDictionaryType::HANGUL_HANJA;
95     else if (rText.EqualsAscii( CONV_TYPE_SCHINESE_TCHINESE ))
96         nRes = ConversionDictionaryType::SCHINESE_TCHINESE;
97     return nRes;
98 }
99 
100 ///////////////////////////////////////////////////////////////////////////
101 
102 class ConvDicXMLImportContext :
103 	public SvXMLImportContext
104 {
105 public:
106     ConvDicXMLImportContext(
107 			ConvDicXMLImport &rImport,
108 			sal_uInt16 nPrfx, const OUString& rLName ) :
109         SvXMLImportContext( rImport, nPrfx, rLName )
110     {
111     }
112 
113     const ConvDicXMLImport & GetConvDicImport() const
114     {
115         return (const ConvDicXMLImport &) GetImport();
116     }
117 
118     ConvDicXMLImport & GetConvDicImport()
119     {
120         return (ConvDicXMLImport &) GetImport();
121     }
122 
123     // SvXMLImportContext
124     virtual void Characters( const OUString &rChars );
125     virtual SvXMLImportContext * CreateChildContext( sal_uInt16 nPrefix, const OUString& rLocalName, const uno::Reference< xml::sax::XAttributeList > &rxAttrList);
126 };
127 
128 
129 class ConvDicXMLDictionaryContext_Impl :
130 	public ConvDicXMLImportContext
131 {
132     sal_Int16		nLanguage;
133     sal_Int16   nConversionType;
134 
135 public:
136     ConvDicXMLDictionaryContext_Impl( ConvDicXMLImport &rImport,
137 			sal_uInt16 nPrefix, const OUString& rLName) :
138         ConvDicXMLImportContext( rImport, nPrefix, rLName )
139     {
140         nLanguage = LANGUAGE_NONE;
141         nConversionType = -1;
142     }
143 
144     // SvXMLImportContext
145     virtual void StartElement( const ::com::sun::star::uno::Reference< ::com::sun::star::xml::sax::XAttributeList >& xAttrList );
146     virtual SvXMLImportContext * CreateChildContext( sal_uInt16 nPrefix, const OUString& rLocalName, const uno::Reference< xml::sax::XAttributeList > &rxAttrList );
147 
148     sal_Int16		GetLanguage() const			{ return nLanguage; }
149 	sal_Int16   GetConversionType() const	{ return nConversionType; }
150 };
151 
152 
153 class ConvDicXMLEntryTextContext_Impl :
154 	public ConvDicXMLImportContext
155 {
156     OUString    aLeftText;
157     sal_Int16   nPropertyType;  // used for Chinese simplified/traditional conversion
158 	ConvDicXMLDictionaryContext_Impl	&rDicContext;
159 
160 public:
161     ConvDicXMLEntryTextContext_Impl(
162 			ConvDicXMLImport &rImport,
163 			sal_uInt16 nPrefix, const OUString& rLName,
164 			ConvDicXMLDictionaryContext_Impl &rParentContext ) :
165         ConvDicXMLImportContext( rImport, nPrefix, rLName ),
166         nPropertyType( ConversionPropertyType::NOT_DEFINED ),
167         rDicContext( rParentContext )
168     {
169     }
170 
171     // SvXMLImportContext
172     virtual void StartElement( const ::com::sun::star::uno::Reference< ::com::sun::star::xml::sax::XAttributeList >& xAttrList );
173     virtual SvXMLImportContext * CreateChildContext( sal_uInt16 nPrefix, const OUString& rLocalName, const uno::Reference< xml::sax::XAttributeList > &rxAttrList );
174 
175 	const OUString &	GetLeftText() const	{ return aLeftText; }
176     sal_Int16           GetPropertyType() const { return nPropertyType; }
177     void                SetPropertyType( sal_Int16 nVal )   { nPropertyType = nVal; }
178 };
179 
180 
181 class ConvDicXMLRightTextContext_Impl :
182 	public ConvDicXMLImportContext
183 {
184     OUString aRightText;
185 	ConvDicXMLEntryTextContext_Impl &rEntryContext;
186 
187 public:
188     ConvDicXMLRightTextContext_Impl(
189 			ConvDicXMLImport &rImport,
190 			sal_uInt16 nPrefix, const OUString& rLName,
191 			ConvDicXMLEntryTextContext_Impl &rParentContext ) :
192         ConvDicXMLImportContext( rImport, nPrefix, rLName ),
193 		rEntryContext( rParentContext )
194     {
195     }
196 
197     // SvXMLImportContext
198     virtual void EndElement();
199     virtual SvXMLImportContext * CreateChildContext( sal_uInt16 nPrefix, const OUString& rLocalName, const uno::Reference< xml::sax::XAttributeList > &rxAttrList );
200     virtual void Characters( const OUString &rChars );
201 
202 	const OUString &	GetRightText() const	{ return aRightText; }
203 	const OUString &	GetLeftText() const		{ return rEntryContext.GetLeftText(); }
204 	ConvDic *			GetDic()				{ return GetConvDicImport().GetDic(); }
205 };
206 
207 ///////////////////////////////////////////////////////////////////////////
208 
209 void ConvDicXMLImportContext::Characters(const OUString & /*rChars*/)
210 {
211     /*
212     Whitespace occurring within the content of token elements is "trimmed"
213     from the ends (i.e. all whitespace at the beginning and end of the
214     content is removed), and "collapsed" internally (i.e. each sequence of
215     1 or more whitespace characters is replaced with one blank character).
216     */
217     //collapsing not done yet!
218 
219     // warning-free code: since the result is not used there is no need for trimming...
220     //const OUString &rChars2 = rChars.trim();
221 }
222 
223 SvXMLImportContext * ConvDicXMLImportContext::CreateChildContext(
224         sal_uInt16 nPrefix, const OUString& rLocalName,
225         const uno::Reference< xml::sax::XAttributeList > & /*rxAttrList*/ )
226 {
227     SvXMLImportContext *pContext = 0;
228     if (nPrefix == XML_NAMESPACE_TCD && rLocalName.equalsAscii( "text-conversion-dictionary" ))
229         pContext = new ConvDicXMLDictionaryContext_Impl( GetConvDicImport(), nPrefix, rLocalName );
230 	else
231         pContext = new SvXMLImportContext( GetImport(), nPrefix, rLocalName );
232     return pContext;
233 }
234 
235 ////////////////////////////////////////
236 
237 void ConvDicXMLDictionaryContext_Impl::StartElement(
238     const uno::Reference< xml::sax::XAttributeList > &rxAttrList )
239 {
240     sal_Int16 nAttrCount = rxAttrList.is() ? rxAttrList->getLength() : 0;
241     for (sal_Int16 i = 0;  i < nAttrCount;  ++i)
242     {
243         OUString aAttrName = rxAttrList->getNameByIndex(i);
244         OUString aLocalName;
245         sal_uInt16 nPrefix = GetImport().GetNamespaceMap().
246                                     GetKeyByAttrName( aAttrName, &aLocalName );
247         OUString aValue = rxAttrList->getValueByIndex(i);
248 
249         if (nPrefix == XML_NAMESPACE_TCD && aLocalName.equalsAscii( "lang" ))
250 			nLanguage = MsLangId::convertIsoStringToLanguage( aValue );
251         else if (nPrefix == XML_NAMESPACE_TCD && aLocalName.equalsAscii( "conversion-type" ))
252 			nConversionType = GetConversionTypeFromText( aValue );
253     }
254     GetConvDicImport().SetLanguage( nLanguage );
255 	GetConvDicImport().SetConversionType( nConversionType );
256 
257 	//!! hack to stop the parser from reading the rest of the file  !!
258 	//!! when only the header (language, conversion type) is needed !!
259 //   if (GetConvDicImport().GetDic() == 0)
260 //        throw uno::RuntimeException();
261 }
262 
263 SvXMLImportContext * ConvDicXMLDictionaryContext_Impl::CreateChildContext(
264         sal_uInt16 nPrefix, const OUString& rLocalName,
265         const uno::Reference< xml::sax::XAttributeList > & /*rxAttrList*/ )
266 {
267 	SvXMLImportContext *pContext = 0;
268 	if (nPrefix == XML_NAMESPACE_TCD  &&  rLocalName.equalsAscii( "entry" ))
269 		pContext = new ConvDicXMLEntryTextContext_Impl( GetConvDicImport(), nPrefix, rLocalName, *this );
270 	else
271 		pContext = new SvXMLImportContext( GetImport(), nPrefix, rLocalName );
272     return pContext;
273 }
274 
275 ////////////////////////////////////////
276 
277 SvXMLImportContext * ConvDicXMLEntryTextContext_Impl::CreateChildContext(
278         sal_uInt16 nPrefix, const OUString& rLocalName,
279         const uno::Reference< xml::sax::XAttributeList > & /*rxAttrList*/ )
280 {
281 	SvXMLImportContext *pContext = 0;
282 	if (nPrefix == XML_NAMESPACE_TCD  &&  rLocalName.equalsAscii( "right-text" ))
283 		pContext = new ConvDicXMLRightTextContext_Impl( GetConvDicImport(), nPrefix, rLocalName, *this );
284 	else
285 		pContext = new SvXMLImportContext( GetImport(), nPrefix, rLocalName );
286     return pContext;
287 }
288 
289 void ConvDicXMLEntryTextContext_Impl::StartElement(
290 		const uno::Reference< xml::sax::XAttributeList >& rxAttrList )
291 {
292     sal_Int16 nAttrCount = rxAttrList.is() ? rxAttrList->getLength() : 0;
293     for (sal_Int16 i = 0;  i < nAttrCount;  ++i)
294     {
295         OUString aAttrName = rxAttrList->getNameByIndex(i);
296         OUString aLocalName;
297         sal_uInt16 nPrefix = GetImport().GetNamespaceMap().
298                                     GetKeyByAttrName( aAttrName, &aLocalName );
299         OUString aValue = rxAttrList->getValueByIndex(i);
300 
301         if (nPrefix == XML_NAMESPACE_TCD && aLocalName.equalsAscii( "left-text" ))
302 			aLeftText = aValue;
303         if (nPrefix == XML_NAMESPACE_TCD && aLocalName.equalsAscii( "property-type" ))
304             nPropertyType = (sal_Int16) aValue.toInt32();
305     }
306 }
307 
308 ////////////////////////////////////////
309 
310 SvXMLImportContext * ConvDicXMLRightTextContext_Impl::CreateChildContext(
311         sal_uInt16 nPrefix, const OUString& rLocalName,
312         const uno::Reference< xml::sax::XAttributeList > & /*rxAttrList*/ )
313 {
314     // leaf: return default (empty) context
315     SvXMLImportContext *pContext = new SvXMLImportContext( GetImport(), nPrefix, rLocalName );
316     return pContext;
317 }
318 
319 void ConvDicXMLRightTextContext_Impl::Characters( const OUString &rChars )
320 {
321     aRightText += rChars;
322 }
323 
324 void ConvDicXMLRightTextContext_Impl::EndElement()
325 {
326 	ConvDic *pDic = GetDic();
327 	if (pDic)
328 		pDic->AddEntry( GetLeftText(), GetRightText() );
329 }
330 
331 
332 ///////////////////////////////////////////////////////////////////////////
333 
334 sal_Bool ConvDicXMLExport::Export()
335 {
336     sal_Bool bRet = sal_False;
337 
338     uno::Reference< document::XExporter > xExporter( this );
339     uno::Reference< document::XFilter > xFilter( xExporter, UNO_QUERY );
340     uno::Sequence< beans::PropertyValue > aProps(0);
341     xFilter->filter( aProps );      // calls exportDoc implicitly
342 
343     return bRet = bSuccess;
344 }
345 
346 
347 sal_uInt32 ConvDicXMLExport::exportDoc( enum ::xmloff::token::XMLTokenEnum /*eClass*/ )
348 {
349     _GetNamespaceMap().Add( A2OU( "tcd" ),
350             A2OU( XML_NAMESPACE_TCD_STRING ), XML_NAMESPACE_TCD );
351 
352     GetDocHandler()->startDocument();
353 
354     // Add xmlns line and some other arguments
355     AddAttribute( _GetNamespaceMap().GetAttrNameByKey( XML_NAMESPACE_TCD ),
356                   _GetNamespaceMap().GetNameByKey( XML_NAMESPACE_TCD ) );
357     AddAttributeASCII( XML_NAMESPACE_TCD, "package", "org.openoffice.Office" );
358 
359     OUString aIsoLang( MsLangId::convertLanguageToIsoString( rDic.nLanguage ) );
360     AddAttribute( XML_NAMESPACE_TCD, "lang", aIsoLang );
361     OUString aConvType( ConversionTypeToText( rDic.nConversionType ) );
362     AddAttribute( XML_NAMESPACE_TCD, "conversion-type", aConvType );
363 
364     //!! block necessary in order to have SvXMLElementExport d-tor called
365     //!! before the call to endDocument
366     {
367         SvXMLElementExport aRoot( *this, XML_NAMESPACE_TCD, "text-conversion-dictionary", sal_True, sal_True );
368         _ExportContent();
369     }
370 
371     GetDocHandler()->endDocument();
372 
373     bSuccess = sal_True;
374     return 0;
375 }
376 
377 
378 void ConvDicXMLExport::_ExportContent()
379 {
380     // aquire sorted list of all keys
381     ConvMapKeySet   aKeySet;
382     ConvMap::iterator aIt;
383     for (aIt = rDic.aFromLeft.begin();  aIt != rDic.aFromLeft.end();  ++aIt)
384         aKeySet.insert( (*aIt).first );
385 
386     ConvMapKeySet::iterator aKeyIt;
387     for (aKeyIt = aKeySet.begin();  aKeyIt != aKeySet.end();  ++aKeyIt)
388     {
389         OUString aLeftText( *aKeyIt );
390         AddAttribute( XML_NAMESPACE_TCD, "left-text", aLeftText );
391         if (rDic.pConvPropType.get())   // property-type list available?
392         {
393             sal_Int16 nPropertyType = -1;
394             PropTypeMap::iterator aIt2 = rDic.pConvPropType->find( aLeftText );
395             if (aIt2 != rDic.pConvPropType->end())
396                 nPropertyType = (*aIt2).second;
397             DBG_ASSERT( nPropertyType, "property-type not found" );
398             if (nPropertyType == -1)
399                 nPropertyType = ConversionPropertyType::NOT_DEFINED;
400             AddAttribute( XML_NAMESPACE_TCD, "property-type", OUString::valueOf( (sal_Int32) nPropertyType ) );
401         }
402         SvXMLElementExport aEntryMain( *this, XML_NAMESPACE_TCD,
403                 "entry" , sal_True, sal_True );
404 
405         pair< ConvMap::iterator, ConvMap::iterator > aRange =
406                 rDic.aFromLeft.equal_range( *aKeyIt );
407         for (aIt = aRange.first;  aIt != aRange.second;  ++aIt)
408         {
409             DBG_ASSERT( *aKeyIt == (*aIt).first, "key <-> entry mismatch" );
410             OUString aRightText( (*aIt).second );
411             SvXMLElementExport aEntryRightText( *this, XML_NAMESPACE_TCD,
412                     "right-text" , sal_True, sal_False );
413             Characters( aRightText );
414         }
415     }
416 }
417 
418 ::rtl::OUString SAL_CALL ConvDicXMLExport::getImplementationName()
419     throw( uno::RuntimeException )
420 {
421     return A2OU( "com.sun.star.lingu2.ConvDicXMLExport" );
422 }
423 
424 ///////////////////////////////////////////////////////////////////////////
425 
426 void SAL_CALL ConvDicXMLImport::startDocument(void)
427     throw( xml::sax::SAXException, uno::RuntimeException )
428 {
429     // register namespace at first possible opportunity
430     GetNamespaceMap().Add( A2OU( "tcd" ),
431             A2OU( XML_NAMESPACE_TCD_STRING ), XML_NAMESPACE_TCD );
432     SvXMLImport::startDocument();
433 }
434 
435 void SAL_CALL ConvDicXMLImport::endDocument(void)
436     throw( xml::sax::SAXException, uno::RuntimeException )
437 {
438     SvXMLImport::endDocument();
439 }
440 
441 SvXMLImportContext * ConvDicXMLImport::CreateContext(
442         sal_uInt16 nPrefix,
443         const rtl::OUString &rLocalName,
444         const uno::Reference < xml::sax::XAttributeList > & /*rxAttrList*/ )
445 {
446 	SvXMLImportContext *pContext = 0;
447     if (nPrefix == XML_NAMESPACE_TCD && rLocalName.equalsAscii( "text-conversion-dictionary" ))
448 		pContext = new ConvDicXMLDictionaryContext_Impl( *this, nPrefix, rLocalName );
449 	else
450 		pContext = new SvXMLImportContext( *this, nPrefix, rLocalName );
451 	return pContext;
452 }
453 
454 
455 OUString SAL_CALL ConvDicXMLImport::getImplementationName()
456     throw( uno::RuntimeException )
457 {
458     return A2OU( "com.sun.star.lingu2.ConvDicXMLImport" );
459 }
460 
461 ///////////////////////////////////////////////////////////////////////////
462 
463