LibreOffice Module linguistic (master) 1
convdic.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20
22#include <i18nlangtag/lang.h>
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>
35
36#include <com/sun/star/linguistic2/ConversionPropertyType.hpp>
37#include <com/sun/star/util/XFlushable.hpp>
38#include <com/sun/star/lang/EventObject.hpp>
39#include <com/sun/star/ucb/SimpleFileAccess.hpp>
40#include <com/sun/star/uno/Reference.h>
41#include <com/sun/star/util/XFlushListener.hpp>
42#include <com/sun/star/io/IOException.hpp>
43#include <com/sun/star/io/XInputStream.hpp>
44#include <com/sun/star/xml/sax/Writer.hpp>
45#include <com/sun/star/xml/sax/InputSource.hpp>
46#include <com/sun/star/xml/sax/SAXParseException.hpp>
47#include <com/sun/star/container/NoSuchElementException.hpp>
48#include <com/sun/star/container/ElementExistException.hpp>
49
50
51#include "convdic.hxx"
52#include "convdicxml.hxx"
53#include <linguistic/misc.hxx>
54#include <utility>
55
56using namespace utl;
57using namespace osl;
58using namespace com::sun::star;
59using namespace com::sun::star::lang;
60using namespace com::sun::star::uno;
61using namespace com::sun::star::linguistic2;
62using namespace linguistic;
63
64
65static void ReadThroughDic( const OUString &rMainURL, ConvDicXMLImport &rImport )
66{
67 if (rMainURL.isEmpty())
68 return;
69 DBG_ASSERT(!INetURLObject( rMainURL ).HasError(), "invalid URL");
70
71 uno::Reference< uno::XComponentContext > xContext( comphelper::getProcessComponentContext() );
72
73 // get xInputStream stream
74 uno::Reference< io::XInputStream > xIn;
75 try
76 {
77 uno::Reference< ucb::XSimpleFileAccess3 > xAccess( ucb::SimpleFileAccess::create(xContext) );
78 xIn = xAccess->openFileRead( rMainURL );
79 }
80 catch (const uno::Exception &)
81 {
82 SAL_WARN( "linguistic", "failed to get input stream" );
83 }
84 if (!xIn.is())
85 return;
86
87 // prepare ParserInputSource
88 xml::sax::InputSource aParserInput;
89 aParserInput.aInputStream = xIn;
90
91 // finally, parser the stream
92 try
93 {
94 rImport.parseStream( aParserInput ); // implicitly calls ConvDicXMLImport::CreateContext
95 }
96 catch( xml::sax::SAXParseException& )
97 {
98 TOOLS_WARN_EXCEPTION("linguistic", "");
99 }
100 catch( xml::sax::SAXException& )
101 {
102 TOOLS_WARN_EXCEPTION("linguistic", "");
103 }
104 catch( io::IOException& )
105 {
106 TOOLS_WARN_EXCEPTION("linguistic", "");
107 }
108}
109
110bool IsConvDic( const OUString &rFileURL, LanguageType &nLang, sal_Int16 &nConvType )
111{
112 bool bRes = false;
113
114 if (rFileURL.isEmpty())
115 return bRes;
116
117 // check if file extension matches CONV_DIC_EXT
118 OUString aExt;
119 sal_Int32 nPos = rFileURL.lastIndexOf( '.' );
120 if (-1 != nPos)
121 aExt = rFileURL.copy( nPos + 1 ).toAsciiLowerCase();
122 if (aExt != CONV_DIC_EXT)
123 return bRes;
124
125 // first argument being 0 should stop the file from being parsed
126 // up to the end (reading all entries) when the required
127 // data (language, conversion type) is found.
128 rtl::Reference<ConvDicXMLImport> pImport = new ConvDicXMLImport( nullptr );
129
130 ReadThroughDic( rFileURL, *pImport ); // will implicitly add the entries
131 bRes = !LinguIsUnspecified( pImport->GetLanguage()) &&
132 pImport->GetConversionType() != -1;
133 DBG_ASSERT( bRes, "conversion dictionary corrupted?" );
134
135 if (bRes)
136 {
137 nLang = pImport->GetLanguage();
138 nConvType = pImport->GetConversionType();
139 }
140
141 return bRes;
142}
143
144
146 OUString aName_,
147 LanguageType nLang,
148 sal_Int16 nConvType,
149 bool bBiDirectional,
150 const OUString &rMainURL) :
151 aFlushListeners( GetLinguMutex() ),
152 aMainURL(rMainURL), aName(std::move(aName_)), nLanguage(nLang),
153 nConversionType(nConvType),
154 nMaxLeftCharCount(0), nMaxRightCharCount(0),
155 bMaxCharCountIsValid(true),
156 bNeedEntries(true),
157 bIsModified(false), bIsActive(false)
158
159{
160 if (bBiDirectional)
161 pFromRight.reset( new ConvMap );
163 pConvPropType.reset( new PropTypeMap );
164
165 if( !rMainURL.isEmpty() )
166 {
167 bool bExists = false;
168 IsReadOnly( rMainURL, &bExists );
169
170 if( !bExists ) // new empty dictionary
171 {
172 bNeedEntries = false;
175 // (Note: empty dictionaries are not just empty files!)
176 Save();
177 }
178 }
179 else
180 {
181 bNeedEntries = false;
182 }
183}
184
185
187{
188}
189
190
192{
193 DBG_ASSERT( !bIsModified, "dictionary is modified. Really do 'Load'?" );
194
196 bNeedEntries = false;
198 ReadThroughDic( aMainURL, *pImport ); // will implicitly add the entries
199 bIsModified = false;
200}
201
202
204{
205 DBG_ASSERT( !bNeedEntries, "saving while entries missing" );
206 if (aMainURL.isEmpty() || bNeedEntries)
207 return;
208 DBG_ASSERT(!INetURLObject( aMainURL ).HasError(), "invalid URL");
209
210 uno::Reference< uno::XComponentContext > xContext( comphelper::getProcessComponentContext() );
211
212 // get XOutputStream stream
213 uno::Reference< io::XStream > xStream;
214 try
215 {
216 uno::Reference< ucb::XSimpleFileAccess3 > xAccess( ucb::SimpleFileAccess::create(xContext) );
217 xStream = xAccess->openFileReadWrite( aMainURL );
218 }
219 catch (const uno::Exception &)
220 {
221 SAL_WARN( "linguistic", "failed to get input stream" );
222 }
223 if (!xStream.is())
224 return;
225
226 std::unique_ptr<SvStream> pStream( utl::UcbStreamHelper::CreateStream( xStream ) );
227
228 // get XML writer
229 uno::Reference< xml::sax::XWriter > xSaxWriter = xml::sax::Writer::create(xContext);
230
231 if (xStream.is())
232 {
233 // connect XML writer to output stream
234 xSaxWriter->setOutputStream( xStream->getOutputStream() );
235
236 // prepare arguments (prepend doc handler to given arguments)
237 rtl::Reference<ConvDicXMLExport> pExport = new ConvDicXMLExport( *this, aMainURL, xSaxWriter );
238 bool bRet = pExport->Export(); // write entries to file
239 DBG_ASSERT( !pStream->GetError(), "I/O error while writing to stream" );
240 if (bRet)
241 bIsModified = false;
242 }
243 DBG_ASSERT( !bIsModified, "dictionary still modified after save. Save failed?" );
244}
245
246
247ConvMap::iterator ConvDic::GetEntry( ConvMap &rMap, const OUString &rFirstText, std::u16string_view rSecondText )
248{
249 std::pair< ConvMap::iterator, ConvMap::iterator > aRange =
250 rMap.equal_range( rFirstText );
251 ConvMap::iterator aPos = rMap.end();
252 for (ConvMap::iterator aIt = aRange.first;
253 aIt != aRange.second && aPos == rMap.end();
254 ++aIt)
255 {
256 if ((*aIt).second == rSecondText)
257 aPos = aIt;
258 }
259 return aPos;
260}
261
262
263bool ConvDic::HasEntry( const OUString &rLeftText, std::u16string_view rRightText )
264{
265 if (bNeedEntries)
266 Load();
267 ConvMap::iterator aIt = GetEntry( aFromLeft, rLeftText, rRightText );
268 return aIt != aFromLeft.end();
269}
270
271
272void ConvDic::AddEntry( const OUString &rLeftText, const OUString &rRightText )
273{
274 if (bNeedEntries)
275 Load();
276
277 DBG_ASSERT(!HasEntry( rLeftText, rRightText), "entry already exists" );
278 aFromLeft .emplace( rLeftText, rRightText );
279 if (pFromRight)
280 pFromRight->emplace( rRightText, rLeftText );
281
283 {
284 if (rLeftText.getLength() > nMaxLeftCharCount)
285 nMaxLeftCharCount = static_cast<sal_Int16>(rLeftText.getLength());
286 if (pFromRight && rRightText.getLength() > nMaxRightCharCount)
287 nMaxRightCharCount = static_cast<sal_Int16>(rRightText.getLength());
288 }
289
290 bIsModified = true;
291}
292
293
294void ConvDic::RemoveEntry( const OUString &rLeftText, const OUString &rRightText )
295{
296 if (bNeedEntries)
297 Load();
298
299 ConvMap::iterator aLeftIt = GetEntry( aFromLeft, rLeftText, rRightText );
300 DBG_ASSERT( aLeftIt != aFromLeft.end(), "left map entry missing" );
301 aFromLeft .erase( aLeftIt );
302
303 if (pFromRight)
304 {
305 ConvMap::iterator aRightIt = GetEntry( *pFromRight, rRightText, rLeftText );
306 DBG_ASSERT( aRightIt != pFromRight->end(), "right map entry missing" );
307 pFromRight->erase( aRightIt );
308 }
309
310 bIsModified = true;
311 bMaxCharCountIsValid = false;
312}
313
314
315OUString SAL_CALL ConvDic::getName( )
316{
317 MutexGuard aGuard( GetLinguMutex() );
318 return aName;
319}
320
321
323{
324 MutexGuard aGuard( GetLinguMutex() );
326}
327
328
329sal_Int16 SAL_CALL ConvDic::getConversionType( )
330{
331 MutexGuard aGuard( GetLinguMutex() );
332 return nConversionType;
333}
334
335
336void SAL_CALL ConvDic::setActive( sal_Bool bActivate )
337{
338 MutexGuard aGuard( GetLinguMutex() );
339 bIsActive = bActivate;
340}
341
342
344{
345 MutexGuard aGuard( GetLinguMutex() );
346 return bIsActive;
347}
348
349
350void SAL_CALL ConvDic::clear( )
351{
352 MutexGuard aGuard( GetLinguMutex() );
353 aFromLeft .clear();
354 if (pFromRight)
355 pFromRight->clear();
356 bNeedEntries = false;
357 bIsModified = true;
361}
362
363
364uno::Sequence< OUString > SAL_CALL ConvDic::getConversions(
365 const OUString& aText,
366 sal_Int32 nStartPos,
367 sal_Int32 nLength,
368 ConversionDirection eDirection,
369 sal_Int32 /*nTextConversionOptions*/ )
370{
371 MutexGuard aGuard( GetLinguMutex() );
372
373 if (!pFromRight && eDirection != ConversionDirection_FROM_LEFT)
374 return uno::Sequence< OUString >();
375
376 if (bNeedEntries)
377 Load();
378
379 OUString aLookUpText( aText.copy(nStartPos, nLength) );
380 ConvMap &rConvMap = eDirection == ConversionDirection_FROM_LEFT ?
382 std::pair< ConvMap::iterator, ConvMap::iterator > aRange =
383 rConvMap.equal_range( aLookUpText );
384
385 std::vector<OUString> aRes;
386 auto nCount = static_cast<size_t>(std::distance(aRange.first, aRange.second));
387 aRes.reserve(nCount);
388
389 std::transform(aRange.first, aRange.second, std::back_inserter(aRes),
390 [](ConvMap::const_reference rEntry) { return rEntry.second; });
391
393}
394
395
396uno::Sequence< OUString > SAL_CALL ConvDic::getConversionEntries(
397 ConversionDirection eDirection )
398{
399 MutexGuard aGuard( GetLinguMutex() );
400
401 if (!pFromRight && eDirection != ConversionDirection_FROM_LEFT)
402 return uno::Sequence< OUString >();
403
404 if (bNeedEntries)
405 Load();
406
407 ConvMap &rConvMap = eDirection == ConversionDirection_FROM_LEFT ?
409 std::vector<OUString> aRes;
410 aRes.reserve(rConvMap.size());
411 for (auto const& elem : rConvMap)
412 {
413 OUString aCurEntry( elem.first );
414 // skip duplicate entries ( duplicate = duplicate entries
415 // respective to the evaluated side (FROM_LEFT or FROM_RIGHT).
416 // Thus if FROM_LEFT is evaluated for pairs (A,B) and (A,C)
417 // only one entry for A will be returned in the result)
418 if (std::find(aRes.begin(), aRes.end(), aCurEntry) == aRes.end())
419 aRes.push_back(aCurEntry);
420 }
421
423}
424
425
426void SAL_CALL ConvDic::addEntry(
427 const OUString& aLeftText,
428 const OUString& aRightText )
429{
430 MutexGuard aGuard( GetLinguMutex() );
431 if (bNeedEntries)
432 Load();
433 if (HasEntry( aLeftText, aRightText ))
434 throw container::ElementExistException();
435 AddEntry( aLeftText, aRightText );
436}
437
438
440 const OUString& aLeftText,
441 const OUString& aRightText )
442{
443 MutexGuard aGuard( GetLinguMutex() );
444 if (bNeedEntries)
445 Load();
446 if (!HasEntry( aLeftText, aRightText ))
447 throw container::NoSuchElementException();
448 RemoveEntry( aLeftText, aRightText );
449}
450
451
452sal_Int16 SAL_CALL ConvDic::getMaxCharCount( ConversionDirection eDirection )
453{
454 MutexGuard aGuard( GetLinguMutex() );
455
456 if (!pFromRight && eDirection == ConversionDirection_FROM_RIGHT)
457 {
458 DBG_ASSERT( nMaxRightCharCount == 0, "max right char count should be 0" );
459 return 0;
460 }
461
462 if (bNeedEntries)
463 Load();
464
466 {
468 for (auto const& elem : aFromLeft)
469 {
470 sal_Int16 nTmp = static_cast<sal_Int16>(elem.first.getLength());
471 if (nTmp > nMaxLeftCharCount)
472 nMaxLeftCharCount = nTmp;
473 }
474
476 if (pFromRight)
477 {
478 for (auto const& elem : *pFromRight)
479 {
480 sal_Int16 nTmp = static_cast<sal_Int16>(elem.first.getLength());
481 if (nTmp > nMaxRightCharCount)
482 nMaxRightCharCount = nTmp;
483 }
484 }
485
487 }
488 sal_Int16 nRes = eDirection == ConversionDirection_FROM_LEFT ?
490 DBG_ASSERT( nRes >= 0, "invalid MaxCharCount" );
491 return nRes;
492}
493
494
496 const OUString& rLeftText,
497 const OUString& rRightText,
498 sal_Int16 nPropertyType )
499{
500 bool bHasElement = HasEntry( rLeftText, rRightText);
501 if (!bHasElement)
502 throw container::NoSuchElementException();
503
504 // currently we assume that entries with the same left text have the
505 // same PropertyType even if the right text is different...
506 if (pConvPropType)
507 pConvPropType->emplace( rLeftText, nPropertyType );
508 bIsModified = true;
509}
510
511
512sal_Int16 SAL_CALL ConvDic::getPropertyType(
513 const OUString& rLeftText,
514 const OUString& rRightText )
515{
516 bool bHasElement = HasEntry( rLeftText, rRightText);
517 if (!bHasElement)
518 throw container::NoSuchElementException();
519
520 sal_Int16 nRes = ConversionPropertyType::NOT_DEFINED;
521 if (pConvPropType)
522 {
523 // still assuming that entries with same left text have same PropertyType
524 // even if they have different right text...
525 PropTypeMap::iterator aIt = pConvPropType->find( rLeftText );
526 if (aIt != pConvPropType->end())
527 nRes = (*aIt).second;
528 }
529 return nRes;
530}
531
532
533void SAL_CALL ConvDic::flush( )
534{
535 MutexGuard aGuard( GetLinguMutex() );
536
537 if (!bIsModified)
538 return;
539
540 Save();
541
542 // notify listeners
543 EventObject aEvtObj;
544 aEvtObj.Source = uno::Reference< XFlushable >( this );
545 aFlushListeners.notifyEach( &util::XFlushListener::flushed, aEvtObj );
546}
547
548
550 const uno::Reference< util::XFlushListener >& rxListener )
551{
552 MutexGuard aGuard( GetLinguMutex() );
553 if (rxListener.is())
554 aFlushListeners.addInterface( rxListener );
555}
556
557
559 const uno::Reference< util::XFlushListener >& rxListener )
560{
561 MutexGuard aGuard( GetLinguMutex() );
562 if (rxListener.is())
563 aFlushListeners.removeInterface( rxListener );
564}
565
566
568{
569 return "com.sun.star.lingu2.ConvDic";
570}
571
572sal_Bool SAL_CALL ConvDic::supportsService( const OUString& rServiceName )
573{
574 return cppu::supportsService(this, rServiceName);
575}
576
577uno::Sequence< OUString > SAL_CALL ConvDic::getSupportedServiceNames( )
578{
579 return { SN_CONV_DICTIONARY };
580}
581
582
583/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Reference< XInputStream > xStream
OUString aMainURL
Definition: convdic.hxx:64
virtual css::lang::Locale SAL_CALL getLocale() override
Definition: convdic.cxx:322
virtual void SAL_CALL addEntry(const OUString &aLeftText, const OUString &aRightText) override
Definition: convdic.cxx:426
sal_Int16 nConversionType
Definition: convdic.hxx:67
sal_Int16 nMaxRightCharCount
Definition: convdic.hxx:69
virtual ~ConvDic() override
Definition: convdic.cxx:186
bool bIsActive
Definition: convdic.hxx:73
void RemoveEntry(const OUString &rLeftText, const OUString &rRightText)
Definition: convdic.cxx:294
virtual ::sal_Int16 SAL_CALL getPropertyType(const OUString &aLeftText, const OUString &aRightText) override
Definition: convdic.cxx:512
bool bMaxCharCountIsValid
Definition: convdic.hxx:70
std::unique_ptr< ConvMap > pFromRight
Definition: convdic.hxx:60
virtual sal_Int16 SAL_CALL getConversionType() override
Definition: convdic.cxx:329
sal_Int16 nMaxLeftCharCount
Definition: convdic.hxx:68
virtual void SAL_CALL removeFlushListener(const css::uno::Reference< css::util::XFlushListener > &l) override
Definition: convdic.cxx:558
friend class ConvDicXMLExport
Definition: convdic.hxx:53
std::unique_ptr< PropTypeMap > pConvPropType
Definition: convdic.hxx:62
virtual OUString SAL_CALL getImplementationName() override
Definition: convdic.cxx:567
void AddEntry(const OUString &rLeftText, const OUString &rRightText)
Definition: convdic.cxx:272
virtual sal_Bool SAL_CALL isActive() override
Definition: convdic.cxx:343
virtual void SAL_CALL clear() override
Definition: convdic.cxx:350
::comphelper::OInterfaceContainerHelper3< css::util::XFlushListener > aFlushListeners
Definition: convdic.hxx:57
OUString aName
Definition: convdic.hxx:65
virtual void SAL_CALL setPropertyType(const OUString &aLeftText, const OUString &aRightText, ::sal_Int16 nPropertyType) override
Definition: convdic.cxx:495
bool bIsModified
Definition: convdic.hxx:72
virtual css::uno::Sequence< OUString > SAL_CALL getConversions(const OUString &aText, sal_Int32 nStartPos, sal_Int32 nLength, css::linguistic2::ConversionDirection eDirection, sal_Int32 nTextConversionOptions) override
Definition: convdic.cxx:364
ConvMap aFromLeft
Definition: convdic.hxx:59
virtual OUString SAL_CALL getName() override
Definition: convdic.cxx:315
static ConvMap::iterator GetEntry(ConvMap &rMap, const OUString &rFirstText, std::u16string_view rSecondText)
Definition: convdic.cxx:247
virtual void SAL_CALL removeEntry(const OUString &aLeftText, const OUString &aRightText) override
Definition: convdic.cxx:439
void Load()
Definition: convdic.cxx:191
LanguageType nLanguage
Definition: convdic.hxx:66
ConvDic(const ConvDic &)
virtual sal_Bool SAL_CALL supportsService(const OUString &ServiceName) override
Definition: convdic.cxx:572
bool bNeedEntries
Definition: convdic.hxx:71
virtual css::uno::Sequence< OUString > SAL_CALL getConversionEntries(css::linguistic2::ConversionDirection eDirection) override
Definition: convdic.cxx:396
virtual void SAL_CALL addFlushListener(const css::uno::Reference< css::util::XFlushListener > &l) override
Definition: convdic.cxx:549
virtual void SAL_CALL flush() override
Definition: convdic.cxx:533
virtual void SAL_CALL setActive(sal_Bool bActivate) override
Definition: convdic.cxx:336
bool HasEntry(const OUString &rLeftText, std::u16string_view rRightText)
Definition: convdic.cxx:263
void Save()
Definition: convdic.cxx:203
virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override
Definition: convdic.cxx:577
virtual sal_Int16 SAL_CALL getMaxCharCount(css::linguistic2::ConversionDirection eDirection) override
Definition: convdic.cxx:452
static css::lang::Locale convertToLocale(LanguageType nLangID, bool bResolveSystem=true)
sal_Int32 addInterface(const css::uno::Reference< ListenerT > &rxIFace)
sal_Int32 removeInterface(const css::uno::Reference< ListenerT > &rxIFace)
void notifyEach(void(SAL_CALL ListenerT::*NotificationMethod)(const EventT &), const EventT &Event)
static std::unique_ptr< SvStream > CreateStream(const OUString &rFileName, StreamMode eOpenMode, css::uno::Reference< css::awt::XWindow > xParentWin=nullptr)
static void ReadThroughDic(const OUString &rMainURL, ConvDicXMLImport &rImport)
Definition: convdic.cxx:65
bool IsConvDic(const OUString &rFileURL, LanguageType &nLang, sal_Int16 &nConvType)
Definition: convdic.cxx:110
constexpr OUStringLiteral CONV_DIC_EXT
Definition: convdic.hxx:33
constexpr OUStringLiteral SN_CONV_DICTIONARY
Definition: convdic.hxx:36
std::unordered_multimap< OUString, OUString > ConvMap
Definition: convdic.hxx:41
std::unordered_multimap< OUString, sal_Int16 > PropTypeMap
Definition: convdic.hxx:42
int nCount
#define DBG_ASSERT(sCon, aError)
#define TOOLS_WARN_EXCEPTION(area, stream)
OUString aName
#define LANGUAGE_CHINESE_TRADITIONAL
#define LANGUAGE_CHINESE_SIMPLIFIED
sal_uInt16 nPos
#define SAL_WARN(area, stream)
css::uno::Sequence< DstElementType > containerToSequence(const SrcType &i_Container)
Reference< XComponentContext > getProcessComponentContext()
bool CPPUHELPER_DLLPUBLIC supportsService(css::lang::XServiceInfo *implementation, rtl::OUString const &name)
bool IsReadOnly(const OUString &rURL, bool *pbExist)
Definition: misc.cxx:385
bool LinguIsUnspecified(LanguageType nLanguage)
Checks if a LanguageType is one of the values that denote absence of language or undetermined languag...
Definition: misc.cxx:88
osl::Mutex & GetLinguMutex()
! multi-thread safe mutex for all platforms !!
Definition: misc.cxx:60
unsigned char sal_Bool
sal_Int32 nLength