tdf#130857 qt weld: Implement QtInstanceWidget::strip_mnemonic
[LibreOffice.git] / oox / source / dump / dumperbase.cxx
blob995346b0914a48cc0f53ec388aef154eb19a6b66
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 .
20 #include <oox/dump/dumperbase.hxx>
22 #include <algorithm>
23 #include <string_view>
25 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
26 #include <com/sun/star/io/TextOutputStream.hpp>
27 #include <com/sun/star/ucb/SimpleFileAccess.hpp>
28 #include <osl/file.hxx>
29 #include <rtl/math.hxx>
30 #include <rtl/tencinfo.h>
31 #include <oox/core/filterbase.hxx>
32 #include <oox/helper/binaryoutputstream.hxx>
33 #include <oox/helper/textinputstream.hxx>
34 #include <tools/time.hxx>
35 #include <o3tl/string_view.hxx>
36 #include <utility>
38 #ifdef DBG_UTIL
40 namespace oox::dump {
42 using namespace ::com::sun::star;
43 using namespace ::com::sun::star::io;
44 using namespace ::com::sun::star::ucb;
45 using namespace ::com::sun::star::uno;
47 using ::oox::core::FilterBase;
49 namespace {
51 const sal_Unicode OOX_DUMP_BOM = 0xFEFF;
52 const sal_Int32 OOX_DUMP_MAXSTRLEN = 80;
53 const sal_Int32 OOX_DUMP_INDENT = 2;
54 const sal_Unicode OOX_DUMP_BINDOT = '.';
55 const sal_Unicode OOX_DUMP_CFG_LISTSEP = ',';
56 const sal_Unicode OOX_DUMP_CFG_QUOTE = '\'';
57 const sal_Unicode OOX_DUMP_LF = '\n';
58 const sal_Unicode OOX_DUMP_ITEMSEP = '=';
59 const sal_Int32 OOX_DUMP_BYTESPERLINE = 16;
60 const sal_Int64 OOX_DUMP_MAXARRAY = 16;
62 } // namespace
64 // file names -----------------------------------------------------------------
66 OUString InputOutputHelper::convertFileNameToUrl( const OUString& rFileName )
68 OUString aFileUrl;
69 if( ::osl::FileBase::getFileURLFromSystemPath( rFileName, aFileUrl ) == ::osl::FileBase::E_None )
70 return aFileUrl;
71 return OUString();
74 sal_Int32 InputOutputHelper::getFileNamePos( std::u16string_view rFileUrl )
76 size_t nSepPos = rFileUrl.find( '/' );
77 return (nSepPos == std::u16string_view::npos) ? 0 : (nSepPos + 1);
80 std::u16string_view InputOutputHelper::getFileNameExtension( std::u16string_view rFileUrl )
82 sal_Int32 nNamePos = getFileNamePos( rFileUrl );
83 size_t nExtPos = rFileUrl.rfind( '.' );
84 if( nExtPos != std::u16string_view::npos && static_cast<sal_Int32>(nExtPos) >= nNamePos )
85 return rFileUrl.substr( nExtPos + 1 );
86 return std::u16string_view();
89 // input streams --------------------------------------------------------------
91 Reference< XInputStream > InputOutputHelper::openInputStream(
92 const Reference< XComponentContext >& rxContext, const OUString& rFileName )
94 Reference< XInputStream > xInStrm;
95 if( rxContext.is() ) try
97 Reference<XSimpleFileAccess3> xFileAccess(SimpleFileAccess::create(rxContext));
98 xInStrm = xFileAccess->openFileRead( rFileName );
100 catch( Exception& )
103 return xInStrm;
106 // output streams -------------------------------------------------------------
108 Reference< XOutputStream > InputOutputHelper::openOutputStream(
109 const Reference< XComponentContext >& rxContext, const OUString& rFileName )
111 Reference< XOutputStream > xOutStrm;
112 if( rxContext.is() ) try
114 Reference<XSimpleFileAccess3> xFileAccess(SimpleFileAccess::create(rxContext));
115 xOutStrm = xFileAccess->openFileWrite( rFileName );
117 catch( Exception& )
120 return xOutStrm;
123 Reference< XTextOutputStream2 > InputOutputHelper::openTextOutputStream(
124 const Reference< XComponentContext >& rxContext, const Reference< XOutputStream >& rxOutStrm, rtl_TextEncoding eTextEnc )
126 Reference< XTextOutputStream2 > xTextOutStrm;
127 const char* pcCharset = rtl_getMimeCharsetFromTextEncoding( eTextEnc );
128 if( rxContext.is() && rxOutStrm.is() && pcCharset ) try
130 xTextOutStrm = TextOutputStream::create(rxContext);
131 xTextOutStrm->setOutputStream( rxOutStrm );
132 xTextOutStrm->setEncoding( OUString::createFromAscii( pcCharset ) );
134 catch( Exception& )
137 return xTextOutStrm;
140 Reference< XTextOutputStream2 > InputOutputHelper::openTextOutputStream(
141 const Reference< XComponentContext >& rxContext, const OUString& rFileName, rtl_TextEncoding eTextEnc )
143 return openTextOutputStream( rxContext, openOutputStream( rxContext, rFileName ), eTextEnc );
146 ItemFormat::ItemFormat() :
147 meDataType( DATATYPE_VOID ),
148 meFmtType( FORMATTYPE_NONE )
152 void ItemFormat::set( DataType eDataType, FormatType eFmtType, const OUString& rItemName )
154 meDataType = eDataType;
155 meFmtType = eFmtType;
156 maItemName = rItemName;
157 maListName.clear();
160 OUStringVector::const_iterator ItemFormat::parse( const OUStringVector& rFormatVec )
162 set( DATATYPE_VOID, FORMATTYPE_NONE, OUString() );
164 OUStringVector::const_iterator aIt = rFormatVec.begin(), aEnd = rFormatVec.end();
165 OUString aDataType, aFmtType;
166 if( aIt != aEnd ) aDataType = *aIt++;
167 if( aIt != aEnd ) aFmtType = *aIt++;
168 if( aIt != aEnd ) maItemName = *aIt++;
169 if( aIt != aEnd ) maListName = *aIt++;
171 meDataType = StringHelper::convertToDataType( aDataType );
172 meFmtType = StringHelper::convertToFormatType( aFmtType );
174 if( meFmtType == FORMATTYPE_NONE )
176 if ( aFmtType == "unused" )
177 set( meDataType, FORMATTYPE_HEX, OOX_DUMP_UNUSED );
178 else if ( aFmtType == "unknown" )
179 set( meDataType, FORMATTYPE_HEX, OOX_DUMP_UNKNOWN );
182 return aIt;
185 OUStringVector ItemFormat::parse( std::u16string_view rFormatStr )
187 OUStringVector aFormatVec;
188 StringHelper::convertStringToStringList( aFormatVec, rFormatStr, false );
189 OUStringVector::const_iterator aIt = parse( aFormatVec );
190 return OUStringVector( aIt, const_cast< const OUStringVector& >( aFormatVec ).end() );
193 // append string to string ----------------------------------------------------
195 void StringHelper::appendChar( OUStringBuffer& rStr, sal_Unicode cChar, sal_Int32 nCount )
197 for( sal_Int32 nIndex = 0; nIndex < nCount; ++nIndex )
198 rStr.append( cChar );
201 void StringHelper::appendString( OUStringBuffer& rStr, std::u16string_view rData, sal_Int32 nWidth, sal_Unicode cFill )
203 appendChar( rStr, cFill, nWidth - rData.size() );
204 rStr.append( rData );
207 // append decimal -------------------------------------------------------------
209 void StringHelper::appendDec( OUStringBuffer& rStr, sal_uInt8 nData, sal_Int32 nWidth, sal_Unicode cFill )
211 appendString( rStr, OUString::number( nData ), nWidth, cFill );
214 void StringHelper::appendDec( OUStringBuffer& rStr, sal_Int8 nData, sal_Int32 nWidth, sal_Unicode cFill )
216 appendString( rStr, OUString::number( nData ), nWidth, cFill );
219 void StringHelper::appendDec( OUStringBuffer& rStr, sal_uInt16 nData, sal_Int32 nWidth, sal_Unicode cFill )
221 appendString( rStr, OUString::number( nData ), nWidth, cFill );
224 void StringHelper::appendDec( OUStringBuffer& rStr, sal_Int16 nData, sal_Int32 nWidth, sal_Unicode cFill )
226 appendString( rStr, OUString::number( nData ), nWidth, cFill );
229 void StringHelper::appendDec( OUStringBuffer& rStr, sal_uInt32 nData, sal_Int32 nWidth, sal_Unicode cFill )
231 appendString( rStr, OUString::number( nData ), nWidth, cFill );
234 void StringHelper::appendDec( OUStringBuffer& rStr, sal_Int32 nData, sal_Int32 nWidth, sal_Unicode cFill )
236 appendString( rStr, OUString::number( nData ), nWidth, cFill );
239 void StringHelper::appendDec( OUStringBuffer& rStr, sal_uInt64 nData, sal_Int32 nWidth, sal_Unicode cFill )
241 /* Values greater than biggest signed 64bit integer will change to
242 negative when converting to sal_Int64. Therefore, the trailing digit
243 will be written separately. */
244 OUStringBuffer aBuffer;
245 if( nData > 9 )
246 aBuffer.append( static_cast<sal_Int64>(nData / 10 ) );
247 aBuffer.append( static_cast< sal_Unicode >( '0' + (nData % 10) ) );
248 appendString( rStr, aBuffer.makeStringAndClear(), nWidth, cFill );
251 void StringHelper::appendDec( OUStringBuffer& rStr, sal_Int64 nData, sal_Int32 nWidth, sal_Unicode cFill )
253 appendString( rStr, OUString::number( nData ), nWidth, cFill );
256 void StringHelper::appendDec( OUStringBuffer& rStr, double fData, sal_Int32 nWidth, sal_Unicode cFill )
258 appendString( rStr, ::rtl::math::doubleToUString( fData, rtl_math_StringFormat_G, 15, '.', true ), nWidth, cFill );
261 // append hexadecimal ---------------------------------------------------------
263 void StringHelper::appendHex( OUStringBuffer& rStr, sal_uInt8 nData, bool bPrefix )
265 static const sal_Unicode spcHexDigits[] = { '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F' };
266 if( bPrefix )
267 rStr.append( "0x" );
268 rStr.append( OUStringChar(spcHexDigits[ (nData >> 4) & 0x0F ] ) + OUStringChar( spcHexDigits[ nData & 0x0F ] ) );
271 void StringHelper::appendHex( OUStringBuffer& rStr, sal_Int8 nData, bool bPrefix )
273 appendHex( rStr, static_cast< sal_uInt8 >( nData ), bPrefix );
276 void StringHelper::appendHex( OUStringBuffer& rStr, sal_uInt16 nData, bool bPrefix )
278 appendHex( rStr, static_cast< sal_uInt8 >( nData >> 8 ), bPrefix );
279 appendHex( rStr, static_cast< sal_uInt8 >( nData ), false );
282 void StringHelper::appendHex( OUStringBuffer& rStr, sal_Int16 nData, bool bPrefix )
284 appendHex( rStr, static_cast< sal_uInt16 >( nData ), bPrefix );
287 void StringHelper::appendHex( OUStringBuffer& rStr, sal_uInt32 nData, bool bPrefix )
289 appendHex( rStr, static_cast< sal_uInt16 >( nData >> 16 ), bPrefix );
290 appendHex( rStr, static_cast< sal_uInt16 >( nData ), false );
293 void StringHelper::appendHex( OUStringBuffer& rStr, sal_Int32 nData, bool bPrefix )
295 appendHex( rStr, static_cast< sal_uInt32 >( nData ), bPrefix );
298 void StringHelper::appendHex( OUStringBuffer& rStr, sal_uInt64 nData, bool bPrefix )
300 appendHex( rStr, static_cast< sal_uInt32 >( nData >> 32 ), bPrefix );
301 appendHex( rStr, static_cast< sal_uInt32 >( nData ), false );
304 void StringHelper::appendHex( OUStringBuffer& rStr, sal_Int64 nData, bool bPrefix )
306 appendHex( rStr, static_cast< sal_uInt64 >( nData ), bPrefix );
309 static sal_uInt64
310 lcl_ConvertDouble(double const f)
312 sal_uInt64 i = sal_uInt64();
313 for (size_t j = 0; j < sizeof(double); ++j)
314 { // hopefully both endian independent and strict aliasing safe
315 reinterpret_cast<char *>(&i)[j] = reinterpret_cast<char const *>(&f)[j];
317 return i;
320 void StringHelper::appendHex( OUStringBuffer& rStr, double fData, bool bPrefix )
322 appendHex( rStr, lcl_ConvertDouble(fData), bPrefix );
325 // append shortened hexadecimal -----------------------------------------------
327 void StringHelper::appendShortHex( OUStringBuffer& rStr, sal_uInt8 nData, bool bPrefix )
329 appendHex( rStr, nData, bPrefix );
332 void StringHelper::appendShortHex( OUStringBuffer& rStr, sal_Int8 nData, bool bPrefix )
334 appendHex( rStr, nData, bPrefix );
337 void StringHelper::appendShortHex( OUStringBuffer& rStr, sal_uInt16 nData, bool bPrefix )
339 if( nData > SAL_MAX_UINT8 )
340 appendHex( rStr, nData, bPrefix );
341 else
342 appendHex( rStr, static_cast< sal_uInt8 >( nData ), bPrefix );
345 void StringHelper::appendShortHex( OUStringBuffer& rStr, sal_Int16 nData, bool bPrefix )
347 appendShortHex( rStr, static_cast< sal_uInt16 >( nData ), bPrefix );
350 void StringHelper::appendShortHex( OUStringBuffer& rStr, sal_uInt32 nData, bool bPrefix )
352 if( nData > SAL_MAX_UINT16 )
353 appendHex( rStr, nData, bPrefix );
354 else
355 appendShortHex( rStr, static_cast< sal_uInt16 >( nData ), bPrefix );
358 void StringHelper::appendShortHex( OUStringBuffer& rStr, sal_Int32 nData, bool bPrefix )
360 appendShortHex( rStr, static_cast< sal_uInt32 >( nData ), bPrefix );
363 void StringHelper::appendShortHex( OUStringBuffer& rStr, sal_uInt64 nData, bool bPrefix )
365 if( nData > SAL_MAX_UINT32 )
366 appendHex( rStr, nData, bPrefix );
367 else
368 appendShortHex( rStr, static_cast< sal_uInt32 >( nData ), bPrefix );
371 void StringHelper::appendShortHex( OUStringBuffer& rStr, sal_Int64 nData, bool bPrefix )
373 appendShortHex( rStr, static_cast< sal_uInt64 >( nData ), bPrefix );
376 void StringHelper::appendShortHex( OUStringBuffer& rStr, double fData, bool bPrefix )
378 appendHex( rStr, fData, bPrefix );
381 // append binary --------------------------------------------------------------
383 void StringHelper::appendBin( OUStringBuffer& rStr, sal_uInt8 nData, bool bDots )
385 for( sal_uInt8 nMask = 0x80; nMask != 0; (nMask >>= 1) &= 0x7F )
387 rStr.append( static_cast< sal_Unicode >( (nData & nMask) ? '1' : '0' ) );
388 if( bDots && (nMask == 0x10) )
389 rStr.append( OOX_DUMP_BINDOT );
393 void StringHelper::appendBin( OUStringBuffer& rStr, sal_Int8 nData, bool bDots )
395 appendBin( rStr, static_cast< sal_uInt8 >( nData ), bDots );
398 void StringHelper::appendBin( OUStringBuffer& rStr, sal_uInt16 nData, bool bDots )
400 appendBin( rStr, static_cast< sal_uInt8 >( nData >> 8 ), bDots );
401 if( bDots )
402 rStr.append( OOX_DUMP_BINDOT );
403 appendBin( rStr, static_cast< sal_uInt8 >( nData ), bDots );
406 void StringHelper::appendBin( OUStringBuffer& rStr, sal_Int16 nData, bool bDots )
408 appendBin( rStr, static_cast< sal_uInt16 >( nData ), bDots );
411 void StringHelper::appendBin( OUStringBuffer& rStr, sal_uInt32 nData, bool bDots )
413 appendBin( rStr, static_cast< sal_uInt16 >( nData >> 16 ), bDots );
414 if( bDots )
415 rStr.append( OOX_DUMP_BINDOT );
416 appendBin( rStr, static_cast< sal_uInt16 >( nData ), bDots );
419 void StringHelper::appendBin( OUStringBuffer& rStr, sal_Int32 nData, bool bDots )
421 appendBin( rStr, static_cast< sal_uInt32 >( nData ), bDots );
424 void StringHelper::appendBin( OUStringBuffer& rStr, sal_uInt64 nData, bool bDots )
426 appendBin( rStr, static_cast< sal_uInt32 >( nData >> 32 ), bDots );
427 if( bDots )
428 rStr.append( OOX_DUMP_BINDOT );
429 appendBin( rStr, static_cast< sal_uInt32 >( nData ), bDots );
432 void StringHelper::appendBin( OUStringBuffer& rStr, sal_Int64 nData, bool bDots )
434 appendBin( rStr, static_cast< sal_uInt64 >( nData ), bDots );
437 void StringHelper::appendBin( OUStringBuffer& rStr, double fData, bool bDots )
439 appendBin( rStr, lcl_ConvertDouble(fData), bDots );
442 // append formatted value -----------------------------------------------------
444 void StringHelper::appendBool( OUStringBuffer& rStr, bool bData )
446 rStr.appendAscii( bData ? "true" : "false" );
449 // encoded text output --------------------------------------------------------
451 void StringHelper::appendCChar( OUStringBuffer& rStr, sal_Unicode cChar, bool bPrefix )
453 if( cChar > 0x00FF )
455 if( bPrefix )
456 rStr.append( "\\u" );
457 appendHex( rStr, static_cast< sal_uInt16 >( cChar ), false );
459 else
461 if( bPrefix )
462 rStr.append( "\\x" );
463 appendHex( rStr, static_cast< sal_uInt8 >( cChar ), false );
467 void StringHelper::appendEncChar( OUStringBuffer& rStr, sal_Unicode cChar, sal_Int32 nCount, bool bPrefix )
469 if( cChar < 0x0020 )
471 // C-style hex code
472 OUStringBuffer aCode;
473 appendCChar( aCode, cChar, bPrefix );
474 OUString aCodeStr = aCode.makeStringAndClear();
475 for( sal_Int32 nIdx = 0; nIdx < nCount; ++nIdx )
476 rStr.append( aCodeStr );
478 else
480 appendChar( rStr, cChar, nCount );
484 void StringHelper::appendEncString( OUStringBuffer& rStr, std::u16string_view rData, bool bPrefix )
486 size_t nBeg = 0;
487 size_t nIdx = 0;
488 size_t nEnd = rData.size();
489 while( nIdx < nEnd )
491 // find next character that needs encoding
492 while( (nIdx < nEnd) && (rData[ nIdx ] >= 0x20) ) ++nIdx;
493 // append portion
494 if( nBeg < nIdx )
496 if( (nBeg == 0) && (nIdx == nEnd) )
497 rStr.append( rData );
498 else
499 rStr.append( rData.substr(nBeg, nIdx - nBeg) );
501 // append characters to be encoded
502 while( (nIdx < nEnd) && (rData[ nIdx ] < 0x20) )
504 appendCChar( rStr, rData[ nIdx ], bPrefix );
505 ++nIdx;
507 // adjust limits
508 nBeg = nIdx;
512 // token list -----------------------------------------------------------------
514 void StringHelper::appendToken( OUStringBuffer& rStr, std::u16string_view rToken, sal_Unicode cSep )
516 if( (rStr.getLength() > 0) && (!rToken.empty()) )
517 rStr.append( cSep );
518 rStr.append( rToken );
521 void StringHelper::appendIndex( OUStringBuffer& rStr, sal_Int64 nIdx )
523 OUStringBuffer aToken;
524 appendDec( aToken, nIdx );
525 rStr.append( "[" + aToken + "]" );
528 std::u16string_view StringHelper::getToken( std::u16string_view rData, sal_Int32& rnPos, sal_Unicode cSep )
530 return trimSpaces( o3tl::getToken(rData, 0, cSep, rnPos ) );
533 void StringHelper::enclose( OUStringBuffer& rStr, sal_Unicode cOpen, sal_Unicode cClose )
535 rStr.insert( 0, cOpen ).append( cClose ? cClose : cOpen );
538 // string conversion ----------------------------------------------------------
540 namespace {
542 sal_Int32 lclIndexOf( std::u16string_view rStr, sal_Unicode cChar, sal_Int32 nStartPos )
544 size_t nIndex = rStr.find( cChar, nStartPos );
545 return (nIndex == std::u16string_view::npos) ? rStr.size() : nIndex;
548 OUString lclTrimQuotedStringList( std::u16string_view rStr )
550 OUStringBuffer aBuffer;
551 size_t nPos = 0;
552 size_t nLen = rStr.size();
553 while( nPos < nLen )
555 if( rStr[ nPos ] == OOX_DUMP_CFG_QUOTE )
557 // quoted string, skip leading quote character
558 ++nPos;
559 // process quoted text and embedded literal quote characters
560 OUStringBuffer aToken;
563 // seek to next quote character and add text portion to token buffer
564 size_t nEnd = lclIndexOf( rStr, OOX_DUMP_CFG_QUOTE, nPos );
565 aToken.append( rStr.substr(nPos, nEnd - nPos) );
566 // process literal quotes
567 while( (nEnd + 1 < nLen) && (rStr[ nEnd ] == OOX_DUMP_CFG_QUOTE) && (rStr[ nEnd + 1 ] == OOX_DUMP_CFG_QUOTE) )
569 aToken.append( OOX_DUMP_CFG_QUOTE );
570 nEnd += 2;
572 // nEnd is start of possible next text portion
573 nPos = nEnd;
575 while( (nPos < nLen) && (rStr[ nPos ] != OOX_DUMP_CFG_QUOTE) );
576 // add token, seek to list separator, ignore text following closing quote
577 aBuffer.append( aToken );
578 nPos = lclIndexOf( rStr, OOX_DUMP_CFG_LISTSEP, nPos );
579 if( nPos < nLen )
580 aBuffer.append( OOX_DUMP_LF );
581 // set current position behind list separator
582 ++nPos;
584 else
586 // find list separator, add token text to buffer
587 size_t nEnd = lclIndexOf( rStr, OOX_DUMP_CFG_LISTSEP, nPos );
588 aBuffer.append( rStr.substr(nPos, nEnd - nPos) );
589 if( nEnd < nLen )
590 aBuffer.append( OOX_DUMP_LF );
591 // set current position behind list separator
592 nPos = nEnd + 1;
596 return aBuffer.makeStringAndClear();
599 } // namespace
601 std::u16string_view StringHelper::trimSpaces( std::u16string_view rStr )
603 size_t nBeg = 0;
604 while( (nBeg < rStr.size()) && ((rStr[ nBeg ] == ' ') || (rStr[ nBeg ] == '\t')) )
605 ++nBeg;
606 size_t nEnd = rStr.size();
607 while( (nEnd > nBeg) && ((rStr[ nEnd - 1 ] == ' ') || (rStr[ nEnd - 1 ] == '\t')) )
608 --nEnd;
609 return rStr.substr( nBeg, nEnd - nBeg );
612 OUString StringHelper::trimTrailingNul( const OUString& rStr )
614 sal_Int32 nLastPos = rStr.getLength() - 1;
615 if( (nLastPos >= 0) && (rStr[ nLastPos ] == 0) )
616 return rStr.copy( 0, nLastPos );
617 return rStr;
620 OString StringHelper::convertToUtf8( std::u16string_view rStr )
622 return OUStringToOString( rStr, RTL_TEXTENCODING_UTF8 );
625 DataType StringHelper::convertToDataType( std::u16string_view rStr )
627 DataType eType = DATATYPE_VOID;
628 if ( rStr == u"int8" )
629 eType = DATATYPE_INT8;
630 else if ( rStr == u"uint8" )
631 eType = DATATYPE_UINT8;
632 else if ( rStr == u"int16" )
633 eType = DATATYPE_INT16;
634 else if ( rStr == u"uint16" )
635 eType = DATATYPE_UINT16;
636 else if ( rStr == u"int32" )
637 eType = DATATYPE_INT32;
638 else if ( rStr == u"uint32" )
639 eType = DATATYPE_UINT32;
640 else if ( rStr == u"int64" )
641 eType = DATATYPE_INT64;
642 else if ( rStr == u"uint64" )
643 eType = DATATYPE_UINT64;
644 else if ( rStr == u"float" )
645 eType = DATATYPE_FLOAT;
646 else if ( rStr == u"double" )
647 eType = DATATYPE_DOUBLE;
648 return eType;
651 FormatType StringHelper::convertToFormatType( std::u16string_view rStr )
653 FormatType eType = FORMATTYPE_NONE;
654 if ( rStr == u"dec" )
655 eType = FORMATTYPE_DEC;
656 else if ( rStr == u"hex" )
657 eType = FORMATTYPE_HEX;
658 else if ( rStr == u"shorthex" )
659 eType = FORMATTYPE_SHORTHEX;
660 else if ( rStr == u"bin" )
661 eType = FORMATTYPE_BIN;
662 else if ( rStr == u"fix" )
663 eType = FORMATTYPE_FIX;
664 else if ( rStr == u"bool" )
665 eType = FORMATTYPE_BOOL;
666 return eType;
669 bool StringHelper::convertFromDec( sal_Int64& ornData, std::u16string_view rData )
671 size_t nPos = 0;
672 size_t nLen = rData.size();
673 bool bNeg = false;
674 if( (nLen > 0) && (rData[ 0 ] == '-') )
676 bNeg = true;
677 ++nPos;
679 ornData = 0;
680 for( ; nPos < nLen; ++nPos )
682 sal_Unicode cChar = rData[ nPos ];
683 if( (cChar < '0') || (cChar > '9') )
684 return false;
685 ornData = (ornData * 10) + (cChar - '0');
687 if( bNeg )
688 ornData *= -1;
689 return true;
692 bool StringHelper::convertFromHex( sal_Int64& ornData, std::u16string_view rData )
694 ornData = 0;
695 for( size_t nPos = 0, nLen = rData.size(); nPos < nLen; ++nPos )
697 sal_Unicode cChar = rData[ nPos ];
698 if( ('0' <= cChar) && (cChar <= '9') )
699 cChar -= '0';
700 else if( ('A' <= cChar) && (cChar <= 'F') )
701 cChar -= ('A' - 10);
702 else if( ('a' <= cChar) && (cChar <= 'f') )
703 cChar -= ('a' - 10);
704 else
705 return false;
706 ornData = (ornData << 4) + cChar;
708 return true;
711 bool StringHelper::convertStringToInt( sal_Int64& ornData, std::u16string_view rData )
713 if( (rData.size() > 2) && (rData[ 0 ] == '0') && ((rData[ 1 ] == 'X') || (rData[ 1 ] == 'x')) )
714 return convertFromHex( ornData, rData.substr( 2 ) );
715 return convertFromDec( ornData, rData );
718 bool StringHelper::convertStringToDouble( double& orfData, std::u16string_view rData )
720 rtl_math_ConversionStatus eStatus = rtl_math_ConversionStatus_Ok;
721 sal_Int32 nSize = 0;
722 sal_Unicode const * pBegin = rData.data();
723 sal_Unicode const * pEnd;
724 orfData = rtl_math_uStringToDouble(pBegin,
725 pBegin + rData.size(),
726 '.', '\0',
727 &eStatus, &pEnd);
728 nSize = static_cast<sal_Int32>(pEnd - pBegin);
729 return (eStatus == rtl_math_ConversionStatus_Ok) && (nSize == static_cast<sal_Int32>(rData.size()));
732 bool StringHelper::convertStringToBool( std::u16string_view rData )
734 if ( rData == u"true" )
735 return true;
736 if ( rData == u"false" )
737 return false;
738 sal_Int64 nData;
739 return convertStringToInt( nData, rData ) && (nData != 0);
742 OUStringPair StringHelper::convertStringToPair( const OUString& rString, sal_Unicode cSep )
744 OUStringPair aPair;
745 if( !rString.isEmpty() )
747 sal_Int32 nEqPos = rString.indexOf( cSep );
748 if( nEqPos < 0 )
750 aPair.first = rString;
752 else
754 aPair.first = StringHelper::trimSpaces( rString.subView( 0, nEqPos ) );
755 aPair.second = StringHelper::trimSpaces( rString.subView( nEqPos + 1 ) );
758 return aPair;
761 void StringHelper::convertStringToStringList( OUStringVector& orVec, std::u16string_view rData, bool bIgnoreEmpty )
763 orVec.clear();
764 OUString aUnquotedData = lclTrimQuotedStringList( rData );
765 sal_Int32 nPos = 0;
766 sal_Int32 nLen = aUnquotedData.getLength();
767 while( (0 <= nPos) && (nPos < nLen) )
769 std::u16string_view aToken = getToken( aUnquotedData, nPos, OOX_DUMP_LF );
770 if( !bIgnoreEmpty || !aToken.empty() )
771 orVec.push_back( OUString(aToken) );
775 void StringHelper::convertStringToIntList( Int64Vector& orVec, std::u16string_view rData, bool bIgnoreEmpty )
777 orVec.clear();
778 OUString aUnquotedData = lclTrimQuotedStringList( rData );
779 sal_Int32 nPos = 0;
780 sal_Int32 nLen = aUnquotedData.getLength();
781 sal_Int64 nData;
782 while( (0 <= nPos) && (nPos < nLen) )
784 bool bOk = convertStringToInt( nData, getToken( aUnquotedData, nPos, OOX_DUMP_LF ) );
785 if( !bIgnoreEmpty || bOk )
786 orVec.push_back( bOk ? nData : 0 );
790 Base::~Base()
794 ConfigItemBase::~ConfigItemBase()
798 void ConfigItemBase::readConfigBlock( TextInputStream& rStrm )
800 readConfigBlockContents( rStrm );
803 void ConfigItemBase::implProcessConfigItemStr(
804 TextInputStream& /*rStrm*/, const OUString& /*rKey*/, const OUString& /*rData*/ )
808 void ConfigItemBase::implProcessConfigItemInt(
809 TextInputStream& /*rStrm*/, sal_Int64 /*nKey*/, const OUString& /*rData*/ )
813 void ConfigItemBase::readConfigBlockContents( TextInputStream& rStrm )
815 bool bLoop = true;
816 while( bLoop && !rStrm.isEof() )
818 OUString aKey, aData;
819 switch( readConfigLine( rStrm, aKey, aData ) )
821 case LINETYPE_DATA:
822 processConfigItem( rStrm, aKey, aData );
823 break;
824 case LINETYPE_END:
825 bLoop = false;
826 break;
831 ConfigItemBase::LineType ConfigItemBase::readConfigLine(
832 TextInputStream& rStrm, OUString& orKey, OUString& orData )
834 OUString aLine;
835 while( !rStrm.isEof() && aLine.isEmpty() )
837 aLine = rStrm.readLine();
838 if( !aLine.isEmpty() && (aLine[ 0 ] == OOX_DUMP_BOM) )
839 aLine = aLine.copy( 1 );
840 aLine = StringHelper::trimSpaces( aLine );
841 if( !aLine.isEmpty() )
843 // ignore comments (starting with hash or semicolon)
844 sal_Unicode cChar = aLine[ 0 ];
845 if( (cChar == '#') || (cChar == ';') )
846 aLine.clear();
850 OUStringPair aPair = StringHelper::convertStringToPair( aLine );
851 orKey = aPair.first;
852 orData = aPair.second;
853 return ( !orKey.isEmpty() && (!orData.isEmpty() || orKey != "end" )) ?
854 LINETYPE_DATA : LINETYPE_END;
857 void ConfigItemBase::processConfigItem(
858 TextInputStream& rStrm, const OUString& rKey, const OUString& rData )
860 sal_Int64 nKey;
861 if( StringHelper::convertStringToInt( nKey, rKey ) )
862 implProcessConfigItemInt( rStrm, nKey, rData );
863 else
864 implProcessConfigItemStr( rStrm, rKey, rData );
867 NameListBase::~NameListBase()
871 void NameListBase::setName( sal_Int64 nKey, const String& rName )
873 implSetName( nKey, rName );
876 void NameListBase::includeList( const NameListRef& rxList )
878 if( rxList )
880 for (auto const& elem : *rxList)
881 maMap[ elem.first ] = elem.second;
882 implIncludeList( *rxList );
886 bool NameListBase::implIsValid() const
888 return true;
891 void NameListBase::implProcessConfigItemStr(
892 TextInputStream& rStrm, const OUString& rKey, const OUString& rData )
894 if ( rKey == "include" )
895 include( rData );
896 else if ( rKey == "exclude" )
897 exclude( rData );
898 else
899 ConfigItemBase::implProcessConfigItemStr( rStrm, rKey, rData );
902 void NameListBase::implProcessConfigItemInt(
903 TextInputStream& /*rStrm*/, sal_Int64 nKey, const OUString& rData )
905 implSetName( nKey, rData );
908 void NameListBase::insertRawName( sal_Int64 nKey, const OUString& rName )
910 maMap[ nKey ] = rName;
913 const OUString* NameListBase::findRawName( sal_Int64 nKey ) const
915 const_iterator aIt = maMap.find( nKey );
916 return (aIt == end()) ? nullptr : &aIt->second;
919 void NameListBase::include( std::u16string_view rListKeys )
921 OUStringVector aVec;
922 StringHelper::convertStringToStringList( aVec, rListKeys, true );
923 for (auto const& elem : aVec)
924 includeList( mrCfgData.getNameList(elem) );
927 void NameListBase::exclude( std::u16string_view rKeys )
929 Int64Vector aVec;
930 StringHelper::convertStringToIntList( aVec, rKeys, true );
931 for (auto const& elem : aVec)
932 maMap.erase(elem);
935 void ItemFormatMap::insertFormats( const NameListRef& rxNameList )
937 if( Base::isValid( rxNameList ) )
939 for (auto const& elemName : *rxNameList)
940 maMap[ elemName.first ].parse( elemName.second );
944 ConstList::ConstList( const SharedConfigData& rCfgData ) :
945 NameListBase( rCfgData ),
946 maDefName( OOX_DUMP_ERR_NONAME ),
947 mbQuoteNames( false )
951 void ConstList::implProcessConfigItemStr(
952 TextInputStream& rStrm, const OUString& rKey, const OUString& rData )
954 if ( rKey == "default" )
955 maDefName = rData; // Sets a default name for unknown keys.
956 else if ( rKey == "quote-names" )
957 setQuoteNames( StringHelper::convertStringToBool( rData ) );
958 else
959 NameListBase::implProcessConfigItemStr( rStrm, rKey, rData );
962 void ConstList::implSetName( sal_Int64 nKey, const OUString& rName )
964 insertRawName( nKey, rName );
967 OUString ConstList::implGetName( const Config& /*rCfg*/, sal_Int64 nKey ) const
969 const OUString* pName = findRawName( nKey );
970 OUString aName = pName ? *pName : maDefName;
971 if( mbQuoteNames )
973 OUStringBuffer aBuffer( aName );
974 StringHelper::enclose( aBuffer, OOX_DUMP_STRQUOTE );
975 aName = aBuffer.makeStringAndClear();
977 return aName;
980 OUString ConstList::implGetNameDbl( const Config& /*rCfg*/, double /*fValue*/ ) const
982 return OUString();
985 void ConstList::implIncludeList( const NameListBase& rList )
987 if( const ConstList* pConstList = dynamic_cast< const ConstList* >( &rList ) )
989 maDefName = pConstList->maDefName;
990 mbQuoteNames = pConstList->mbQuoteNames;
994 MultiList::MultiList( const SharedConfigData& rCfgData ) :
995 ConstList( rCfgData ),
996 mbIgnoreEmpty( true )
1000 void MultiList::setNamesFromVec( sal_Int64 nStartKey, const OUStringVector& rNames )
1002 sal_Int64 nKey = nStartKey;
1003 for (auto const& name : rNames)
1005 if( !mbIgnoreEmpty || !name.isEmpty() )
1006 insertRawName( nKey, name);
1007 ++nKey;
1011 void MultiList::implProcessConfigItemStr(
1012 TextInputStream& rStrm, const OUString& rKey, const OUString& rData )
1014 if ( rKey == "ignore-empty" )
1015 mbIgnoreEmpty = StringHelper::convertStringToBool( rData );
1016 else
1017 ConstList::implProcessConfigItemStr( rStrm, rKey, rData );
1020 void MultiList::implSetName( sal_Int64 nKey, const OUString& rName )
1022 OUStringVector aNames;
1023 StringHelper::convertStringToStringList( aNames, rName, false );
1024 setNamesFromVec( nKey, aNames );
1027 FlagsList::FlagsList( const SharedConfigData& rCfgData ) :
1028 NameListBase( rCfgData ),
1029 mnIgnore( 0 )
1033 void FlagsList::implProcessConfigItemStr(
1034 TextInputStream& rStrm, const OUString& rKey, const OUString& rData )
1036 if ( rKey == "ignore" )
1038 sal_Int64 nIgnore;
1039 if( StringHelper::convertStringToInt( nIgnore, rData ) )
1040 setIgnoreFlags( nIgnore );
1042 else
1044 NameListBase::implProcessConfigItemStr( rStrm, rKey, rData );
1048 void FlagsList::implSetName( sal_Int64 nKey, const OUString& rName )
1050 if( (nKey != 0) && ((nKey & (nKey - 1)) == 0) ) // only a single bit set?
1051 insertRawName( nKey, rName );
1054 OUString FlagsList::implGetName( const Config& /*rCfg*/, sal_Int64 nKey ) const
1056 sal_Int64 nFound = mnIgnore;
1057 OUStringBuffer aName;
1058 // add known flags
1059 for( const_iterator aIt = begin(), aEnd = end(); aIt != aEnd; ++aIt )
1061 sal_Int64 nMask = aIt->first;
1062 setFlag( nFound, nMask );
1063 if( !getFlag( mnIgnore, nMask ) )
1065 const OUString& rFlagName = aIt->second;
1066 bool bOnOff = rFlagName.startsWith(":");
1067 bool bFlag = getFlag( nKey, nMask );
1068 if( bOnOff )
1070 StringHelper::appendToken( aName, rFlagName.subView( 1 ) );
1071 aName.appendAscii( bFlag ? ":on" : ":off" );
1073 else
1075 bool bNegated = rFlagName.startsWith("!");
1076 sal_Int32 nBothSep = bNegated ? rFlagName.indexOf( '!', 1 ) : -1;
1077 if( bFlag )
1079 if( !bNegated )
1080 StringHelper::appendToken( aName, rFlagName );
1081 else if( nBothSep > 0 )
1082 StringHelper::appendToken( aName, rFlagName.subView( nBothSep + 1 ) );
1084 else if( bNegated )
1086 if( nBothSep > 0 )
1087 StringHelper::appendToken( aName, rFlagName.subView( 1, nBothSep - 1 ) );
1088 else
1089 StringHelper::appendToken( aName, rFlagName.subView( 1 ) );
1094 // add unknown flags
1095 setFlag( nKey, nFound, false );
1096 if( nKey != 0 )
1098 OUStringBuffer aUnknown( OUString::Concat(OOX_DUMP_UNKNOWN) + OUStringChar(OOX_DUMP_ITEMSEP) );
1099 StringHelper::appendShortHex( aUnknown, nKey );
1100 StringHelper::enclose( aUnknown, '(', ')' );
1101 StringHelper::appendToken( aName, aUnknown );
1103 return aName.makeStringAndClear();
1106 OUString FlagsList::implGetNameDbl( const Config& /*rCfg*/, double /*fValue*/ ) const
1108 return OUString();
1111 void FlagsList::implIncludeList( const NameListBase& rList )
1113 if( const FlagsList* pFlagsList = dynamic_cast< const FlagsList* >( &rList ) )
1114 mnIgnore = pFlagsList->mnIgnore;
1117 bool CombiList::ExtItemFormatKey::operator<( const ExtItemFormatKey& rRight ) const
1119 return (mnKey < rRight.mnKey) || ((mnKey == rRight.mnKey) && (maFilter < rRight.maFilter));
1122 CombiList::CombiList( const SharedConfigData& rCfgData ) :
1123 FlagsList( rCfgData )
1127 void CombiList::implSetName( sal_Int64 nKey, const OUString& rName )
1129 if( (nKey & (nKey - 1)) != 0 ) // more than a single bit set?
1131 ::std::set< ExtItemFormatKey > aItemKeys;
1132 ExtItemFormat aItemFmt;
1133 OUStringVector aRemain = aItemFmt.parse( rName );
1134 for (auto const& elemRemain : aRemain)
1136 OUStringPair aPair = StringHelper::convertStringToPair(elemRemain);
1137 if ( aPair.first == "noshift" )
1139 aItemFmt.mbShiftValue = StringHelper::convertStringToBool( aPair.second );
1141 else if ( aPair.first == "filter" )
1143 OUStringPair aFilter = StringHelper::convertStringToPair( aPair.second, '~' );
1144 ExtItemFormatKey aKey( nKey );
1145 if( !aFilter.first.isEmpty() && StringHelper::convertStringToInt( aKey.maFilter.first, aFilter.first ) &&
1146 !aFilter.second.isEmpty() && StringHelper::convertStringToInt( aKey.maFilter.second, aFilter.second ) )
1148 if( aKey.maFilter.first == 0 )
1149 aKey.maFilter.second = 0;
1150 aItemKeys.insert( aKey );
1154 if( aItemKeys.empty() )
1155 aItemKeys.insert( ExtItemFormatKey( nKey ) );
1156 for (auto const& itemKey : aItemKeys)
1157 maFmtMap[itemKey] = aItemFmt;
1159 else
1161 FlagsList::implSetName( nKey, rName );
1165 OUString CombiList::implGetName( const Config& rCfg, sal_Int64 nKey ) const
1167 sal_Int64 nFound = 0;
1168 OUStringBuffer aName;
1169 // add known flag fields
1170 for (auto const& fmt : maFmtMap)
1172 const ExtItemFormatKey& rMapKey = fmt.first;
1173 sal_Int64 nMask = rMapKey.mnKey;
1174 if( (nMask != 0) && ((nKey & rMapKey.maFilter.first) == rMapKey.maFilter.second) )
1176 const ExtItemFormat& rItemFmt = fmt.second;
1178 sal_uInt64 nUFlags = static_cast< sal_uInt64 >( nKey );
1179 sal_uInt64 nUMask = static_cast< sal_uInt64 >( nMask );
1180 if( rItemFmt.mbShiftValue )
1181 while( (nUMask & 1) == 0 ) { nUFlags >>= 1; nUMask >>= 1; }
1183 sal_uInt64 nUValue = nUFlags & nUMask;
1184 sal_Int64 nSValue = static_cast< sal_Int64 >( nUValue );
1185 if( getFlag< sal_uInt64 >( nUValue, (nUMask + 1) >> 1 ) )
1186 setFlag( nSValue, static_cast< sal_Int64 >( ~nUMask ) );
1188 OUStringBuffer aItem( rItemFmt.maItemName );
1189 OUStringBuffer aValue;
1190 switch( rItemFmt.meDataType )
1192 case DATATYPE_INT8: StringHelper::appendValue( aValue, static_cast< sal_Int8 >( nSValue ), rItemFmt.meFmtType ); break;
1193 case DATATYPE_UINT8: StringHelper::appendValue( aValue, static_cast< sal_uInt8 >( nUValue ), rItemFmt.meFmtType ); break;
1194 case DATATYPE_INT16: StringHelper::appendValue( aValue, static_cast< sal_Int16 >( nSValue ), rItemFmt.meFmtType ); break;
1195 case DATATYPE_UINT16: StringHelper::appendValue( aValue, static_cast< sal_uInt16 >( nUValue ), rItemFmt.meFmtType ); break;
1196 case DATATYPE_INT32: StringHelper::appendValue( aValue, static_cast< sal_Int32 >( nSValue ), rItemFmt.meFmtType ); break;
1197 case DATATYPE_UINT32: StringHelper::appendValue( aValue, static_cast< sal_uInt32 >( nUValue ), rItemFmt.meFmtType ); break;
1198 case DATATYPE_INT64: StringHelper::appendValue( aValue, nSValue, rItemFmt.meFmtType ); break;
1199 case DATATYPE_UINT64: StringHelper::appendValue( aValue, nUValue, rItemFmt.meFmtType ); break;
1200 case DATATYPE_FLOAT: StringHelper::appendValue( aValue, static_cast< float >( nSValue ), rItemFmt.meFmtType ); break;
1201 case DATATYPE_DOUBLE: StringHelper::appendValue( aValue, static_cast< double >( nSValue ), rItemFmt.meFmtType ); break;
1202 default:;
1204 StringHelper::appendToken( aItem, aValue, OOX_DUMP_ITEMSEP );
1205 if( !rItemFmt.maListName.isEmpty() )
1207 OUString aValueName = rCfg.getName( rItemFmt.maListName, static_cast< sal_Int64 >( nUValue ) );
1208 StringHelper::appendToken( aItem, aValueName, OOX_DUMP_ITEMSEP );
1210 StringHelper::enclose( aItem, '(', ')' );
1211 StringHelper::appendToken( aName, aItem );
1212 setFlag( nFound, nMask );
1215 setFlag( nKey, nFound, false );
1216 StringHelper::appendToken( aName, FlagsList::implGetName( rCfg, nKey ) );
1217 return aName.makeStringAndClear();
1220 void CombiList::implIncludeList( const NameListBase& rList )
1222 if( const CombiList* pCombiList = dynamic_cast< const CombiList* >( &rList ) )
1223 maFmtMap = pCombiList->maFmtMap;
1224 FlagsList::implIncludeList( rList );
1227 UnitConverter::UnitConverter( const SharedConfigData& rCfgData ) :
1228 NameListBase( rCfgData ),
1229 mfFactor( 1.0 )
1233 void UnitConverter::implSetName( sal_Int64 /*nKey*/, const OUString& /*rName*/ )
1235 // nothing to do
1238 OUString UnitConverter::implGetName( const Config& rCfg, sal_Int64 nKey ) const
1240 return implGetNameDbl( rCfg, static_cast< double >( nKey ) );
1243 OUString UnitConverter::implGetNameDbl( const Config& /*rCfg*/, double fValue ) const
1245 OUStringBuffer aValue;
1246 StringHelper::appendDec( aValue, mfFactor * fValue );
1247 aValue.append( maUnitName );
1248 return aValue.makeStringAndClear();
1251 void UnitConverter::implIncludeList( const NameListBase& /*rList*/ )
1255 const NameListRef & NameListWrapper::getNameList( const Config& rCfg ) const
1257 if (!mxList)
1258 mxList = rCfg.getNameList( maName );
1259 return mxList;
1262 SharedConfigData::SharedConfigData( const OUString& rFileName,
1263 const Reference< XComponentContext >& rxContext, StorageRef xRootStrg,
1264 OUString aSysFileName ) :
1265 mxContext( rxContext ),
1266 mxRootStrg(std::move( xRootStrg )),
1267 maSysFileName(std::move( aSysFileName )),
1268 mbLoaded( false )
1270 OUString aFileUrl = InputOutputHelper::convertFileNameToUrl( rFileName );
1271 if( !aFileUrl.isEmpty() )
1273 sal_Int32 nNamePos = InputOutputHelper::getFileNamePos( aFileUrl );
1274 maConfigPath = aFileUrl.copy( 0, nNamePos );
1275 mbLoaded = readConfigFile( aFileUrl );
1279 SharedConfigData::~SharedConfigData()
1283 const OUString* SharedConfigData::getOption( const OUString& rKey ) const
1285 ConfigDataMap::const_iterator aIt = maConfigData.find( rKey );
1286 return (aIt == maConfigData.end()) ? nullptr : &aIt->second;
1289 void SharedConfigData::setNameList( const OUString& rListName, const NameListRef& rxList )
1291 if( !rListName.isEmpty() )
1292 maNameLists[ rListName ] = rxList;
1295 void SharedConfigData::eraseNameList( const OUString& rListName )
1297 maNameLists.erase( rListName );
1300 NameListRef SharedConfigData::getNameList( const OUString& rListName ) const
1302 NameListRef xList;
1303 NameListMap::const_iterator aIt = maNameLists.find( rListName );
1304 if( aIt != maNameLists.end() )
1305 xList = aIt->second;
1306 return xList;
1309 bool SharedConfigData::implIsValid() const
1311 return mbLoaded && mxContext.is() && mxRootStrg && !maSysFileName.isEmpty();
1314 void SharedConfigData::implProcessConfigItemStr(
1315 TextInputStream& rStrm, const OUString& rKey, const OUString& rData )
1317 if ( rKey == "include-config-file" )
1318 readConfigFile( maConfigPath + rData );
1319 else if ( rKey == "constlist" )
1320 readNameList< ConstList >( rStrm, rData );
1321 else if ( rKey == "multilist" )
1322 readNameList< MultiList >( rStrm, rData );
1323 else if ( rKey == "flagslist" )
1324 readNameList< FlagsList >( rStrm, rData );
1325 else if ( rKey == "combilist" )
1326 readNameList< CombiList >( rStrm, rData );
1327 else if ( rKey == "shortlist" )
1328 createShortList( rData );
1329 else if ( rKey == "unitconverter" )
1330 createUnitConverter( rData );
1331 else
1332 maConfigData[ rKey ] = rData;
1335 bool SharedConfigData::readConfigFile( const OUString& rFileUrl )
1337 bool bLoaded = maConfigFiles.count( rFileUrl ) > 0;
1338 if( !bLoaded )
1340 Reference< XInputStream > xInStrm = InputOutputHelper::openInputStream( mxContext, rFileUrl );
1341 TextInputStream aTxtStrm( mxContext, xInStrm, RTL_TEXTENCODING_UTF8 );
1342 if( !aTxtStrm.isEof() )
1344 maConfigFiles.insert( rFileUrl );
1345 readConfigBlockContents( aTxtStrm );
1346 bLoaded = true;
1349 return bLoaded;
1352 void SharedConfigData::createShortList( std::u16string_view rData )
1354 OUStringVector aDataVec;
1355 StringHelper::convertStringToStringList( aDataVec, rData, false );
1356 if( aDataVec.size() < 3 )
1357 return;
1359 sal_Int64 nStartKey;
1360 if( StringHelper::convertStringToInt( nStartKey, aDataVec[ 1 ] ) )
1362 std::shared_ptr< MultiList > xList = createNameList< MultiList >( aDataVec[ 0 ] );
1363 if( xList )
1365 aDataVec.erase( aDataVec.begin(), aDataVec.begin() + 2 );
1366 xList->setNamesFromVec( nStartKey, aDataVec );
1371 void SharedConfigData::createUnitConverter( std::u16string_view rData )
1373 OUStringVector aDataVec;
1374 StringHelper::convertStringToStringList( aDataVec, rData, false );
1375 if( aDataVec.size() < 2 )
1376 return;
1378 OUString aFactor = aDataVec[ 1 ];
1379 bool bRecip = aFactor.startsWith("/");
1380 if( bRecip )
1381 aFactor = aFactor.copy( 1 );
1382 double fFactor;
1383 if( StringHelper::convertStringToDouble( fFactor, aFactor ) && (fFactor != 0.0) )
1385 std::shared_ptr< UnitConverter > xList = createNameList< UnitConverter >( aDataVec[ 0 ] );
1386 if( xList )
1388 xList->setFactor( bRecip ? (1.0 / fFactor) : fFactor );
1389 if( aDataVec.size() >= 3 )
1390 xList->setUnitName( aDataVec[ 2 ] );
1395 Config::Config( const char* pcEnvVar, const FilterBase& rFilter )
1397 construct( pcEnvVar, rFilter );
1400 Config::Config( const char* pcEnvVar, const Reference< XComponentContext >& rxContext, const StorageRef& rxRootStrg, const OUString& rSysFileName )
1402 construct( pcEnvVar, rxContext, rxRootStrg, rSysFileName );
1405 Config::~Config()
1409 void Config::construct( const char* pcEnvVar, const FilterBase& rFilter )
1411 if( !rFilter.getFileUrl().isEmpty() )
1412 construct( pcEnvVar, rFilter.getComponentContext(), rFilter.getStorage(), rFilter.getFileUrl() );
1415 void Config::construct( const char* pcEnvVar, const Reference< XComponentContext >& rxContext, const StorageRef& rxRootStrg, const OUString& rSysFileName )
1417 if( pcEnvVar && rxRootStrg && !rSysFileName.isEmpty() )
1418 if( const char* pcFileName = ::getenv( pcEnvVar ) )
1419 mxCfgData = std::make_shared<SharedConfigData>( OUString::createFromAscii( pcFileName ), rxContext, rxRootStrg, rSysFileName );
1422 const OUString& Config::getStringOption( const String& rKey, const OUString& rDefault ) const
1424 const OUString* pData = implGetOption( rKey );
1425 return pData ? *pData : rDefault;
1428 bool Config::getBoolOption( const String& rKey, bool bDefault ) const
1430 const OUString* pData = implGetOption( rKey );
1431 return pData ? StringHelper::convertStringToBool( *pData ) : bDefault;
1434 bool Config::isDumperEnabled() const
1436 return getBoolOption( "enable-dumper", false );
1439 bool Config::isImportEnabled() const
1441 return getBoolOption( "enable-import", true );
1444 void Config::eraseNameList( const String& rListName )
1446 mxCfgData->eraseNameList( rListName );
1449 NameListRef Config::getNameList( const String& rListName ) const
1451 return mxCfgData->getNameList( rListName );
1454 bool Config::implIsValid() const
1456 return isValid( mxCfgData );
1459 const OUString* Config::implGetOption( const OUString& rKey ) const
1461 return mxCfgData->getOption( rKey );
1464 Output::Output( const Reference< XComponentContext >& rxContext, const OUString& rFileName ) :
1465 mxStrm( InputOutputHelper::openTextOutputStream( rxContext, rFileName, RTL_TEXTENCODING_UTF8 ) ),
1466 mnCol( 0 ),
1467 mnItemLevel( 0 ),
1468 mnMultiLevel( 0 ),
1469 mnItemIdx( 0 ),
1470 mnLastItem( 0 )
1472 if( mxStrm.is() )
1473 mxStrm->writeString( OUString( OOX_DUMP_BOM ) );
1476 void Output::newLine()
1478 if( maLine.getLength() > 0 )
1480 mxStrm->writeString( maIndent );
1481 maLine.append( '\n' );
1482 mxStrm->writeString( maLine.makeStringAndClear() );
1483 mnCol = 0;
1484 mnLastItem = 0;
1488 void Output::emptyLine( size_t nCount )
1490 for( size_t nIdx = 0; nIdx < nCount; ++nIdx )
1491 mxStrm->writeString( OUString('\n') );
1494 void Output::incIndent()
1496 OUStringBuffer aBuffer( maIndent );
1497 StringHelper::appendChar( aBuffer, ' ', OOX_DUMP_INDENT );
1498 maIndent = aBuffer.makeStringAndClear();
1501 void Output::decIndent()
1503 if( maIndent.getLength() >= OOX_DUMP_INDENT )
1504 maIndent = maIndent.copy( OOX_DUMP_INDENT );
1507 void Output::startTable( sal_Int32 nW1 )
1509 startTable( 1, &nW1 );
1512 void Output::startTable( sal_Int32 nW1, sal_Int32 nW2 )
1514 sal_Int32 pnColWidths[ 2 ];
1515 pnColWidths[ 0 ] = nW1;
1516 pnColWidths[ 1 ] = nW2;
1517 startTable( 2, pnColWidths );
1520 void Output::startTable( sal_Int32 nW1, sal_Int32 nW2, sal_Int32 nW3, sal_Int32 nW4 )
1522 sal_Int32 pnColWidths[ 4 ];
1523 pnColWidths[ 0 ] = nW1;
1524 pnColWidths[ 1 ] = nW2;
1525 pnColWidths[ 2 ] = nW3;
1526 pnColWidths[ 3 ] = nW4;
1527 startTable( 4, pnColWidths );
1530 void Output::startTable( size_t nColCount, const sal_Int32* pnColWidths )
1532 maColPos.clear();
1533 maColPos.push_back( 0 );
1534 sal_Int32 nColPos = 0;
1535 for( size_t nCol = 0; nCol < nColCount; ++nCol )
1537 nColPos = nColPos + pnColWidths[ nCol ];
1538 maColPos.push_back( nColPos );
1542 void Output::tab()
1544 tab( mnCol + 1 );
1547 void Output::tab( size_t nCol )
1549 mnCol = nCol;
1550 if( mnCol < maColPos.size() )
1552 sal_Int32 nColPos = maColPos[ mnCol ];
1553 if( maLine.getLength() >= nColPos )
1554 maLine.setLength( ::std::max< sal_Int32 >( nColPos - 1, 0 ) );
1555 StringHelper::appendChar( maLine, ' ', nColPos - maLine.getLength() );
1557 else
1559 StringHelper::appendChar( maLine, ' ', 2 );
1563 void Output::endTable()
1565 maColPos.clear();
1568 void Output::resetItemIndex( sal_Int64 nIdx )
1570 mnItemIdx = nIdx;
1573 void Output::startItem( const String& rItemName )
1575 if( mnItemLevel == 0 )
1577 if( (mnMultiLevel > 0) && (maLine.getLength() > 0) )
1578 tab();
1579 if( rItemName.has() )
1581 writeItemName( rItemName );
1582 writeChar( OOX_DUMP_ITEMSEP );
1585 ++mnItemLevel;
1586 mnLastItem = maLine.getLength();
1589 void Output::contItem()
1591 if( mnItemLevel > 0 )
1593 if( (maLine.getLength() == 0) || (maLine[ maLine.getLength() - 1 ] != OOX_DUMP_ITEMSEP) )
1594 writeChar( OOX_DUMP_ITEMSEP );
1595 mnLastItem = maLine.getLength();
1599 void Output::endItem()
1601 if( mnItemLevel > 0 )
1603 maLastItem = maLine.copy( mnLastItem ).makeStringAndClear();
1604 if( maLastItem.isEmpty() && mnLastItem > 0 && maLine[ mnLastItem - 1 ] == OOX_DUMP_ITEMSEP )
1605 maLine.setLength( mnLastItem - 1 );
1606 --mnItemLevel;
1608 if( mnItemLevel == 0 )
1610 if( mnMultiLevel == 0 )
1611 newLine();
1613 else
1614 contItem();
1617 void Output::startMultiItems()
1619 ++mnMultiLevel;
1622 void Output::endMultiItems()
1624 if( mnMultiLevel > 0 )
1625 --mnMultiLevel;
1626 if( mnMultiLevel == 0 )
1627 newLine();
1630 void Output::writeChar( sal_Unicode cChar, sal_Int32 nCount )
1632 StringHelper::appendEncChar( maLine, cChar, nCount );
1635 void Output::writeAscii( const char* pcStr )
1637 if( pcStr )
1638 maLine.appendAscii( pcStr );
1641 void Output::writeString( std::u16string_view rStr )
1643 StringHelper::appendEncString( maLine, rStr );
1646 void Output::writeArray( const sal_uInt8* pnData, std::size_t nSize, sal_Unicode cSep )
1648 const sal_uInt8* pnEnd = pnData ? (pnData + nSize) : nullptr;
1649 for( const sal_uInt8* pnByte = pnData; pnByte < pnEnd; ++pnByte )
1651 if( pnByte > pnData )
1652 writeChar( cSep );
1653 writeHex( *pnByte, false );
1657 void Output::writeBool( bool bData )
1659 StringHelper::appendBool( maLine, bData );
1662 void Output::writeDateTime( const util::DateTime& rDateTime )
1664 writeDec( rDateTime.Year, 4, '0' );
1665 writeChar( '-' );
1666 writeDec( rDateTime.Month, 2, '0' );
1667 writeChar( '-' );
1668 writeDec( rDateTime.Day, 2, '0' );
1669 writeChar( 'T' );
1670 writeDec( rDateTime.Hours, 2, '0' );
1671 writeChar( ':' );
1672 writeDec( rDateTime.Minutes, 2, '0' );
1673 writeChar( ':' );
1674 writeDec( rDateTime.Seconds, 2, '0' );
1677 bool Output::implIsValid() const
1679 return mxStrm.is();
1682 void Output::writeItemName( const String& rItemName )
1684 if( rItemName.has() && (rItemName[ 0 ] == '#') )
1686 writeString( rItemName.subView( 1 ) );
1687 StringHelper::appendIndex( maLine, mnItemIdx++ );
1689 else
1690 writeString( rItemName );
1693 StorageIterator::StorageIterator( StorageRef xStrg ) :
1694 mxStrg(std::move( xStrg ))
1696 if( mxStrg )
1697 mxStrg->getElementNames( maNames );
1698 maIt = maNames.begin();
1701 StorageIterator::~StorageIterator()
1705 StorageIterator& StorageIterator::operator++()
1707 if( maIt != maNames.end() )
1708 ++maIt;
1709 return *this;
1712 OUString StorageIterator::getName() const
1714 OUString aName;
1715 if( maIt != maNames.end() )
1716 aName = *maIt;
1717 return aName;
1720 bool StorageIterator::isStream() const
1722 return isValid() && mxStrg->openInputStream( *maIt ).is();
1725 bool StorageIterator::isStorage() const
1727 if( !isValid() )
1728 return false;
1729 StorageRef xStrg = mxStrg->openSubStorage( *maIt, false );
1730 return xStrg && xStrg->isStorage();
1733 bool StorageIterator::implIsValid() const
1735 return mxStrg && mxStrg->isStorage() && (maIt != maNames.end());
1738 ObjectBase::~ObjectBase()
1742 void ObjectBase::construct( const ConfigRef& rxConfig )
1744 mxConfig = rxConfig;
1747 void ObjectBase::construct( const ObjectBase& rParent )
1749 *this = rParent;
1752 void ObjectBase::dump()
1754 if( isValid() )
1755 implDump();
1758 bool ObjectBase::implIsValid() const
1760 return isValid( mxConfig );
1763 void ObjectBase::implDump()
1767 void StorageObjectBase::construct( const ObjectBase& rParent, const StorageRef& rxStrg, const OUString& rSysPath )
1769 ObjectBase::construct( rParent );
1770 mxStrg = rxStrg;
1771 maSysPath = rSysPath;
1774 void StorageObjectBase::construct( const ObjectBase& rParent )
1776 ObjectBase::construct( rParent );
1777 if( ObjectBase::implIsValid() )
1779 mxStrg = cfg().getRootStorage();
1780 maSysPath = cfg().getSysFileName();
1784 bool StorageObjectBase::implIsValid() const
1786 return mxStrg && !maSysPath.isEmpty() && ObjectBase::implIsValid();
1789 void StorageObjectBase::implDump()
1791 bool bIsStrg = mxStrg->isStorage();
1792 bool bIsRoot = mxStrg->isRootStorage();
1793 Reference< XInputStream > xBaseStrm;
1794 if( !bIsStrg )
1795 xBaseStrm = mxStrg->openInputStream( OUString() );
1797 OUString aSysOutPath = maSysPath;
1798 if( bIsRoot ) try
1800 aSysOutPath += OOX_DUMP_DUMPEXT;
1801 Reference<XSimpleFileAccess3> xFileAccess(SimpleFileAccess::create(getContext()));
1802 xFileAccess->kill( aSysOutPath );
1804 catch( Exception& )
1808 if( bIsStrg )
1810 extractStorage( mxStrg, OUString(), aSysOutPath );
1812 else if( xBaseStrm.is() )
1814 BinaryInputStreamRef xInStrm( std::make_shared<BinaryXInputStream>( xBaseStrm, false ) );
1815 xInStrm->seekToStart();
1816 implDumpBaseStream( xInStrm, aSysOutPath );
1820 void StorageObjectBase::implDumpStream( const Reference< XInputStream >&, const OUString&, const OUString&, const OUString& )
1824 void StorageObjectBase::implDumpStorage( const StorageRef& rxStrg, const OUString& rStrgPath, const OUString& rSysPath )
1826 extractStorage( rxStrg, rStrgPath, rSysPath );
1829 void StorageObjectBase::implDumpBaseStream( const BinaryInputStreamRef&, const OUString& )
1833 void StorageObjectBase::addPreferredStream( const String& rStrmName )
1835 if( rStrmName.has() )
1836 maPreferred.emplace_back( rStrmName, false );
1839 void StorageObjectBase::addPreferredStorage( const String& rStrgPath )
1841 if( rStrgPath.has() )
1842 maPreferred.emplace_back( rStrgPath, true );
1845 OUString StorageObjectBase::getSysFileName(
1846 std::u16string_view rStrmName, std::u16string_view rSysOutPath )
1848 // encode all characters < 0x20
1849 OUStringBuffer aBuffer;
1850 StringHelper::appendEncString( aBuffer, rStrmName, false );
1852 // replace all characters reserved in file system
1853 OUString aFileName = aBuffer.makeStringAndClear();
1854 static const sal_Unicode spcReserved[] = { '/', '\\', ':', '*', '?', '<', '>', '|' };
1855 for(const sal_Unicode cChar : spcReserved)
1856 aFileName = aFileName.replace(cChar, '_');
1858 // build full path
1859 return OUString::Concat(rSysOutPath) + "/" + aFileName;
1862 void StorageObjectBase::extractStream( StorageBase& rStrg, const OUString& rStrgPath, const OUString& rStrmName, const OUString& rSysFileName )
1864 BinaryXInputStream aInStrm( rStrg.openInputStream( rStrmName ), true );
1865 if( !aInStrm.isEof() )
1867 BinaryXOutputStream aOutStrm( InputOutputHelper::openOutputStream( getContext(), rSysFileName ), true );
1868 if( !aOutStrm.isEof() )
1869 aInStrm.copyToStream( aOutStrm );
1871 Reference< XInputStream > xDumpStrm = InputOutputHelper::openInputStream( getContext(), rSysFileName );
1872 if( xDumpStrm.is() )
1873 implDumpStream( xDumpStrm, rStrgPath, rStrmName, rSysFileName );
1876 void StorageObjectBase::extractStorage( const StorageRef& rxStrg, const OUString& rStrgPath, const OUString& rSysPath )
1878 // create directory in file system
1879 ::osl::FileBase::RC eRes = ::osl::Directory::create( rSysPath );
1880 if( (eRes != ::osl::FileBase::E_None) && (eRes != ::osl::FileBase::E_EXIST) )
1881 return;
1883 // process preferred storages and streams in root storage first
1884 if( rStrgPath.isEmpty() )
1886 for (auto const& elemPreferred : maPreferred)
1887 extractItem( rxStrg, rStrgPath, elemPreferred.maName, rSysPath, elemPreferred.mbStorage, !elemPreferred.mbStorage );
1890 // process children of the storage
1891 for( StorageIterator aIt( rxStrg ); aIt.isValid(); ++aIt )
1893 // skip processed preferred items
1894 OUString aItemName = aIt.getName();
1895 bool bFound = false;
1896 if( rStrgPath.isEmpty() )
1898 for (auto const& elemPreferred : maPreferred)
1900 bFound = elemPreferred.maName == aItemName;
1901 if (bFound)
1902 break;
1905 if( !bFound )
1906 extractItem( rxStrg, rStrgPath, aItemName, rSysPath, aIt.isStorage(), aIt.isStream() );
1910 void StorageObjectBase::extractItem( const StorageRef& rxStrg, const OUString& rStrgPath, const OUString& rItemName, std::u16string_view rSysPath, bool bIsStrg, bool bIsStrm )
1912 OUString aSysFileName = getSysFileName( rItemName, rSysPath );
1913 if( bIsStrg )
1915 OUStringBuffer aStrgPath( rStrgPath );
1916 StringHelper::appendToken( aStrgPath, rItemName, '/' );
1917 implDumpStorage( rxStrg->openSubStorage( rItemName, false ), aStrgPath.makeStringAndClear(), aSysFileName );
1919 else if( bIsStrm )
1921 extractStream( *rxStrg, rStrgPath, rItemName, aSysFileName );
1925 OutputObjectBase::~OutputObjectBase()
1929 void OutputObjectBase::construct( const ObjectBase& rParent, const OUString& rSysFileName )
1931 ObjectBase::construct( rParent );
1932 if( ObjectBase::implIsValid() )
1934 maSysFileName = rSysFileName;
1935 mxOut = std::make_shared<Output>( getContext(), rSysFileName + OOX_DUMP_DUMPEXT );
1939 void OutputObjectBase::construct( const OutputObjectBase& rParent )
1941 *this = rParent;
1944 bool OutputObjectBase::implIsValid() const
1946 return isValid( mxOut ) && ObjectBase::implIsValid();
1949 void OutputObjectBase::writeEmptyItem( const String& rName )
1951 ItemGuard aItem( mxOut, rName );
1954 void OutputObjectBase::writeInfoItem( const String& rName, const String& rData )
1956 ItemGuard aItem( mxOut, rName );
1957 mxOut->writeString( rData );
1960 void OutputObjectBase::writeCharItem( const String& rName, sal_Unicode cData )
1962 ItemGuard aItem( mxOut, rName );
1963 mxOut->writeChar( OOX_DUMP_STRQUOTE );
1964 mxOut->writeChar( cData );
1965 mxOut->writeChar( OOX_DUMP_STRQUOTE );
1968 void OutputObjectBase::writeStringItem( const String& rName, std::u16string_view rData )
1970 ItemGuard aItem( mxOut, rName );
1971 mxOut->writeAscii( "(len=" );
1972 mxOut->writeDec( sal_Int32(rData.size()) );
1973 mxOut->writeAscii( ")," );
1974 OUStringBuffer aValue( rData.substr( 0, ::std::min( sal_Int32(rData.size()), OOX_DUMP_MAXSTRLEN ) ) );
1975 StringHelper::enclose( aValue, OOX_DUMP_STRQUOTE );
1976 mxOut->writeString( aValue.makeStringAndClear() );
1977 if( rData.size() > OOX_DUMP_MAXSTRLEN )
1978 mxOut->writeAscii( ",cut" );
1981 void OutputObjectBase::writeArrayItem( const String& rName, const sal_uInt8* pnData, std::size_t nSize, sal_Unicode cSep )
1983 ItemGuard aItem( mxOut, rName );
1984 mxOut->writeArray( pnData, nSize, cSep );
1987 void OutputObjectBase::writeDateTimeItem( const String& rName, const util::DateTime& rDateTime )
1989 ItemGuard aItem( mxOut, rName );
1990 mxOut->writeDateTime( rDateTime );
1993 void OutputObjectBase::writeGuidItem( const String& rName, const OUString& rGuid )
1995 ItemGuard aItem( mxOut, rName );
1996 mxOut->writeString( rGuid );
1997 aItem.cont();
1998 mxOut->writeString( cfg().getStringOption( rGuid, OUString() ) );
2001 InputObjectBase::~InputObjectBase()
2005 void InputObjectBase::construct( const ObjectBase& rParent, const BinaryInputStreamRef& rxStrm, const OUString& rSysFileName )
2007 OutputObjectBase::construct( rParent, rSysFileName );
2008 mxStrm = rxStrm;
2011 void InputObjectBase::construct( const OutputObjectBase& rParent, const BinaryInputStreamRef& rxStrm )
2013 OutputObjectBase::construct( rParent );
2014 mxStrm = rxStrm;
2017 void InputObjectBase::construct( const InputObjectBase& rParent )
2019 *this = rParent;
2022 bool InputObjectBase::implIsValid() const
2024 return mxStrm && OutputObjectBase::implIsValid();
2027 void InputObjectBase::skipBlock( sal_Int64 nBytes, bool bShowSize )
2029 sal_Int64 nEndPos = ::std::min< sal_Int64 >( mxStrm->tell() + nBytes, mxStrm->size() );
2030 if( mxStrm->tell() < nEndPos )
2032 if( bShowSize )
2033 writeDecItem( "skipped-data-size", static_cast< sal_uInt64 >( nEndPos - mxStrm->tell() ) );
2034 mxStrm->seek( nEndPos );
2038 void InputObjectBase::dumpRawBinary( sal_Int64 nBytes, bool bShowOffset, bool bStream )
2040 TableGuard aTabGuard( mxOut,
2041 bShowOffset ? 12 : 0,
2042 3 * OOX_DUMP_BYTESPERLINE / 2 + 1,
2043 3 * OOX_DUMP_BYTESPERLINE / 2 + 1,
2044 OOX_DUMP_BYTESPERLINE / 2 + 1 );
2046 sal_Int64 nMaxShowSize = cfg().getIntOption< sal_Int64 >(
2047 bStream ? "max-binary-stream-size" : "max-binary-data-size", SAL_MAX_INT64 );
2049 bool bSeekable = mxStrm->size() >= 0;
2050 sal_Int64 nEndPos = bSeekable ? ::std::min< sal_Int64 >( mxStrm->tell() + nBytes, mxStrm->size() ) : 0;
2051 sal_Int64 nDumpEnd = bSeekable ? ::std::min< sal_Int64 >( mxStrm->tell() + nMaxShowSize, nEndPos ) : nMaxShowSize;
2052 sal_Int64 nPos = bSeekable ? mxStrm->tell() : 0;
2053 bool bLoop = true;
2055 while( bLoop && (nPos < nDumpEnd) )
2057 mxOut->writeHex( static_cast< sal_uInt32 >( nPos ) );
2058 mxOut->tab();
2060 sal_uInt8 pnLineData[ OOX_DUMP_BYTESPERLINE ];
2061 sal_Int32 nLineSize = bSeekable ? ::std::min( static_cast< sal_Int32 >( nDumpEnd - mxStrm->tell() ), OOX_DUMP_BYTESPERLINE ) : OOX_DUMP_BYTESPERLINE;
2062 sal_Int32 nReadSize = mxStrm->readMemory( pnLineData, nLineSize );
2063 bLoop = nReadSize == nLineSize;
2064 nPos += nReadSize;
2066 if( nReadSize > 0 )
2068 const sal_uInt8* pnByte = nullptr;
2069 const sal_uInt8* pnEnd = nullptr;
2070 for( pnByte = pnLineData, pnEnd = pnLineData + nReadSize; pnByte != pnEnd; ++pnByte )
2072 if( (pnByte - pnLineData) == (OOX_DUMP_BYTESPERLINE / 2) ) mxOut->tab();
2073 mxOut->writeHex( *pnByte, false );
2074 mxOut->writeChar( ' ' );
2077 aTabGuard.tab( 3 );
2078 for( pnByte = pnLineData, pnEnd = pnLineData + nReadSize; pnByte != pnEnd; ++pnByte )
2080 if( (pnByte - pnLineData) == (OOX_DUMP_BYTESPERLINE / 2) ) mxOut->tab();
2081 mxOut->writeChar( static_cast< sal_Unicode >( (*pnByte < 0x20) ? '.' : *pnByte ) );
2083 mxOut->newLine();
2087 // skip undumped data
2088 if( bSeekable )
2089 skipBlock( nEndPos - mxStrm->tell() );
2092 void InputObjectBase::dumpBinary( const String& rName, sal_Int64 nBytes, bool bShowOffset )
2095 MultiItemsGuard aMultiGuard( mxOut );
2096 writeEmptyItem( rName );
2097 writeDecItem( "size", nBytes );
2099 IndentGuard aIndGuard( mxOut );
2100 dumpRawBinary( nBytes, bShowOffset );
2103 void InputObjectBase::dumpRemaining( sal_Int64 nBytes )
2105 if( nBytes > 0 )
2107 if( cfg().getBoolOption( "show-trailing-unknown", true ) )
2108 dumpBinary( "remaining-data", nBytes, false );
2109 else
2110 skipBlock( nBytes );
2114 void InputObjectBase::dumpRemainingTo( sal_Int64 nPos )
2116 if( mxStrm->isEof() || (mxStrm->tell() > nPos) )
2117 writeInfoItem( "stream-state", OOX_DUMP_ERR_STREAM );
2118 else
2119 dumpRemaining( nPos - mxStrm->tell() );
2120 mxStrm->seek( nPos );
2123 void InputObjectBase::dumpRemainingStream()
2125 dumpRemainingTo( mxStrm->size() );
2128 void InputObjectBase::dumpArray( const String& rName, sal_Int32 nBytes, sal_Unicode cSep )
2130 sal_Int32 nDumpSize = getLimitedValue< sal_Int32, sal_Int64 >( mxStrm->size() - mxStrm->tell(), 0, nBytes );
2131 if( nDumpSize > OOX_DUMP_MAXARRAY )
2133 dumpBinary( rName, nBytes, false );
2135 else if( nDumpSize > 1 )
2137 sal_uInt8 pnData[ OOX_DUMP_MAXARRAY ];
2138 mxStrm->readMemory( pnData, nDumpSize );
2139 writeArrayItem( rName, pnData, nDumpSize, cSep );
2141 else if( nDumpSize == 1 )
2142 dumpHex< sal_uInt8 >( rName );
2145 sal_Unicode InputObjectBase::dumpUnicode( const String& rName )
2147 sal_uInt16 nChar = mxStrm->readuInt16();
2148 sal_Unicode cChar = static_cast< sal_Unicode >( nChar );
2149 writeCharItem( rName( "char" ), cChar );
2150 return cChar;
2153 OUString InputObjectBase::dumpCharArray( const String& rName, sal_Int32 nLen, rtl_TextEncoding eTextEnc, bool bHideTrailingNul )
2155 sal_Int32 nDumpSize = getLimitedValue< sal_Int32, sal_Int64 >( mxStrm->size() - mxStrm->tell(), 0, nLen );
2156 OUString aString;
2157 if( nDumpSize > 0 )
2159 ::std::vector< char > aBuffer( static_cast< std::size_t >( nLen ) + 1 );
2160 sal_Int32 nCharsRead = mxStrm->readMemory(aBuffer.data(), nLen);
2161 aBuffer[ nCharsRead ] = 0;
2162 aString = OStringToOUString(std::string_view(aBuffer.data()), eTextEnc);
2164 if( bHideTrailingNul )
2165 aString = StringHelper::trimTrailingNul( aString );
2166 writeStringItem( rName( "text" ), aString );
2167 return aString;
2170 OUString InputObjectBase::dumpUnicodeArray( const String& rName, sal_Int32 nLen, bool bHideTrailingNul )
2172 OUStringBuffer aBuffer;
2173 for( sal_Int32 nIndex = 0; !mxStrm->isEof() && (nIndex < nLen); ++nIndex )
2175 aBuffer.append( static_cast< sal_Unicode >( mxStrm->readuInt16() ) );
2177 OUString aString = aBuffer.makeStringAndClear();
2178 if( bHideTrailingNul )
2179 aString = StringHelper::trimTrailingNul( aString );
2180 writeStringItem( rName( "text" ), aString );
2181 return aString;
2184 util::DateTime InputObjectBase::dumpFileTime( const String& rName )
2186 util::DateTime aDateTime;
2188 ItemGuard aItem( mxOut, rName( "file-time" ) );
2189 sal_Int64 nFileTime = dumpDec< sal_Int64 >( EMPTY_STRING );
2190 // file time is in 10^-7 seconds (100 nanoseconds), convert to nanoseconds
2191 nFileTime *= 100;
2192 // entire days
2193 sal_Int64 nDays = nFileTime / sal_Int64( ::tools::Time::nanoSecPerDay );
2194 // number of entire years
2195 sal_Int64 nYears = (nDays - (nDays / (4 * 365)) + (nDays / (100 * 365)) - (nDays / (400 * 365))) / 365;
2196 // remaining days in the year
2197 sal_Int64 nDaysInYear = nDays - (nYears * 365 + nYears / 4 - nYears / 100 + nYears / 400);
2198 // the year (file dates start from 1601-01-01)
2199 aDateTime.Year = static_cast< sal_uInt16 >( 1601 + nYears );
2200 // leap year?
2201 bool bLeap = ((aDateTime.Year % 4 == 0) && (aDateTime.Year % 100 != 0)) || (aDateTime.Year % 400 == 0);
2202 // static arrays with number of days in month
2203 static const sal_Int64 spnDaysInMonth[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
2204 static const sal_Int64 spnDaysInMonthL[] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
2205 const sal_Int64* pnDaysInMonth = bLeap ? spnDaysInMonthL : spnDaysInMonth;
2206 // the month
2207 aDateTime.Month = 1;
2208 while( nDaysInYear >= *pnDaysInMonth )
2210 nDaysInYear -= *pnDaysInMonth++;
2211 ++aDateTime.Month;
2213 // the day
2214 aDateTime.Day = static_cast< sal_uInt16 >( nDaysInYear + 1 );
2215 // number of nanoseconds in the day
2216 sal_Int64 nTimeInDay = nFileTime % sal_Int64( ::tools::Time::nanoSecPerDay );
2217 // nanoseconds
2218 aDateTime.NanoSeconds = static_cast< sal_uInt32 >( nTimeInDay % ::tools::Time::nanoSecPerSec );
2219 nTimeInDay /= ::tools::Time::nanoSecPerSec;
2220 // seconds
2221 aDateTime.Seconds = static_cast< sal_uInt16 >( nTimeInDay % ::tools::Time::secondPerMinute );
2222 nTimeInDay /= ::tools::Time::secondPerMinute;
2223 // minutes
2224 aDateTime.Minutes = static_cast< sal_uInt16 >( nTimeInDay % ::tools::Time::minutePerHour );
2225 nTimeInDay /= ::tools::Time::minutePerHour;
2226 // hours
2227 aDateTime.Hours = static_cast< sal_uInt16 >( nTimeInDay );
2229 writeDateTimeItem( EMPTY_STRING, aDateTime );
2230 return aDateTime;
2233 OUString InputObjectBase::dumpGuid( const String& rName )
2235 OUStringBuffer aBuffer;
2236 sal_uInt32 nData32;
2237 sal_uInt16 nData16;
2238 sal_uInt8 nData8;
2240 nData32 = mxStrm->readuInt32();
2241 StringHelper::appendHex( aBuffer, nData32, false );
2242 aBuffer.append( '-' );
2243 nData16 = mxStrm->readuInt16();
2244 StringHelper::appendHex( aBuffer, nData16, false );
2245 aBuffer.append( '-' );
2246 nData16 = mxStrm->readuInt16();
2247 StringHelper::appendHex( aBuffer, nData16, false );
2248 aBuffer.append( '-' );
2249 nData8 = mxStrm->readuChar();
2250 StringHelper::appendHex( aBuffer, nData8, false );
2251 nData8 = mxStrm->readuChar( );
2252 StringHelper::appendHex( aBuffer, nData8, false );
2253 aBuffer.append( '-' );
2254 for( int nIndex = 0; nIndex < 6; ++nIndex )
2256 nData8 = mxStrm->readuChar( );
2257 StringHelper::appendHex( aBuffer, nData8, false );
2259 StringHelper::enclose( aBuffer, '{', '}' );
2260 OUString aGuid = aBuffer.makeStringAndClear();
2261 writeGuidItem( rName( "guid" ), aGuid );
2262 return aGuid;
2265 void InputObjectBase::dumpItem( const ItemFormat& rItemFmt )
2267 switch( rItemFmt.meDataType )
2269 case DATATYPE_VOID: break;
2270 case DATATYPE_INT8: dumpValue< sal_Int8 >( rItemFmt ); break;
2271 case DATATYPE_UINT8: dumpValue< sal_uInt8 >( rItemFmt ); break;
2272 case DATATYPE_INT16: dumpValue< sal_Int16 >( rItemFmt ); break;
2273 case DATATYPE_UINT16: dumpValue< sal_uInt16 >( rItemFmt ); break;
2274 case DATATYPE_INT32: dumpValue< sal_Int32 >( rItemFmt ); break;
2275 case DATATYPE_UINT32: dumpValue< sal_uInt32 >( rItemFmt ); break;
2276 case DATATYPE_INT64: dumpValue< sal_Int64 >( rItemFmt ); break;
2277 case DATATYPE_UINT64: dumpValue< sal_uInt64 >( rItemFmt ); break;
2278 case DATATYPE_FLOAT: dumpValue< float >( rItemFmt ); break;
2279 case DATATYPE_DOUBLE: dumpValue< double >( rItemFmt ); break;
2280 default:;
2284 BinaryStreamObject::BinaryStreamObject( const ObjectBase& rParent, const BinaryInputStreamRef& rxStrm, const OUString& rSysFileName )
2286 InputObjectBase::construct( rParent, rxStrm, rSysFileName );
2289 void BinaryStreamObject::dumpBinaryStream( bool bShowOffset )
2291 mxStrm->seekToStart();
2292 dumpRawBinary( mxStrm->size(), bShowOffset, true );
2293 mxOut->emptyLine();
2296 void BinaryStreamObject::implDump()
2298 dumpBinaryStream();
2301 void TextStreamObjectBase::construct( const ObjectBase& rParent,
2302 const BinaryInputStreamRef& rxStrm, rtl_TextEncoding eTextEnc, const OUString& rSysFileName )
2304 InputObjectBase::construct( rParent, rxStrm, rSysFileName );
2305 constructTextStrmObj( eTextEnc );
2308 void TextStreamObjectBase::construct( const OutputObjectBase& rParent,
2309 const BinaryInputStreamRef& rxStrm, rtl_TextEncoding eTextEnc )
2311 InputObjectBase::construct( rParent, rxStrm );
2312 constructTextStrmObj( eTextEnc );
2315 bool TextStreamObjectBase::implIsValid() const
2317 return InputObjectBase::implIsValid() && mxTextStrm;
2320 void TextStreamObjectBase::implDump()
2322 implDumpText( *mxTextStrm );
2325 void TextStreamObjectBase::constructTextStrmObj( rtl_TextEncoding eTextEnc )
2327 if( mxStrm )
2328 mxTextStrm = std::make_shared<TextInputStream>( getContext(), *mxStrm, eTextEnc );
2331 TextLineStreamObject::TextLineStreamObject( const ObjectBase& rParent,
2332 const BinaryInputStreamRef& rxStrm, rtl_TextEncoding eTextEnc, const OUString& rSysFileName )
2334 TextStreamObjectBase::construct( rParent, rxStrm, eTextEnc, rSysFileName );
2337 TextLineStreamObject::TextLineStreamObject( const OutputObjectBase& rParent,
2338 const BinaryInputStreamRef& rxStrm, rtl_TextEncoding eTextEnc )
2340 TextStreamObjectBase::construct( rParent, rxStrm, eTextEnc );
2343 void TextLineStreamObject::implDumpText( TextInputStream& rTextStrm )
2345 sal_uInt32 nLine = 0;
2346 while( !rTextStrm.isEof() )
2348 OUString aLine = rTextStrm.readLine();
2349 if( !rTextStrm.isEof() || !aLine.isEmpty() )
2350 implDumpLine( aLine, ++nLine );
2354 void TextLineStreamObject::implDumpLine( std::u16string_view rLine, sal_uInt32 nLine )
2356 TableGuard aTabGuard( mxOut, 8 );
2357 mxOut->writeDec( nLine, 6 );
2358 mxOut->tab();
2359 mxOut->writeString( rLine );
2360 mxOut->newLine();
2363 XmlStreamObject::XmlStreamObject( const ObjectBase& rParent,
2364 const BinaryInputStreamRef& rxStrm, const OUString& rSysFileName )
2366 TextStreamObjectBase::construct( rParent, rxStrm, RTL_TEXTENCODING_UTF8, rSysFileName );
2369 void XmlStreamObject::implDumpText( TextInputStream& rTextStrm )
2371 /* Buffers a start element and the following element text. Needed to dump
2372 matching start/end elements and the element text on the same line. */
2373 OUStringBuffer aOldStartElem;
2374 // special handling for VML
2375 bool bIsVml = o3tl::equalsIgnoreAsciiCase(InputOutputHelper::getFileNameExtension( maSysFileName ), u"vml");
2377 while( !rTextStrm.isEof() )
2379 // get the next element and the following element text from text stream
2380 OUString aElem = rTextStrm.readToChar( '>', true ).trim();
2381 OUString aText = rTextStrm.readToChar( '<', false );
2383 // remove multiple whitespace from element
2384 sal_Int32 nPos = 0;
2385 while( nPos < aElem.getLength() )
2387 while( (nPos < aElem.getLength()) && (aElem[ nPos ] >= 32) ) ++nPos;
2388 if( nPos < aElem.getLength() )
2389 aElem = aElem.subView( 0, nPos ) + OUStringChar(' ') + o3tl::trim(aElem.subView( nPos ));
2390 ++nPos;
2393 sal_Int32 nElemLen = aElem.getLength();
2394 if( (nElemLen >= 2) && (aElem[ 0 ] == '<') && (aElem[ nElemLen - 1 ] == '>') )
2396 // determine type of the element
2397 bool bSimpleElem = (aElem[ 1 ] == '!') || (aElem[ 1 ] == '?') || (aElem[ nElemLen - 2 ] == '/') ||
2398 (bIsVml && (nElemLen == 4) && (aElem[ 1 ] == 'b') && (aElem[ 2 ] == 'r'));
2399 bool bStartElem = !bSimpleElem && (aElem[ 1 ] != '/');
2400 bool bEndElem = !bSimpleElem && !bStartElem;
2402 /* Start element or simple element: flush old start element and
2403 its text from previous iteration, and start a new indentation
2404 level for the new element. Trim whitespace and line breaks from
2405 the text of the old start element. */
2406 if( (bSimpleElem || bStartElem) && (aOldStartElem.getLength() > 0) )
2408 mxOut->writeString( o3tl::trim(aOldStartElem.makeStringAndClear()) );
2409 mxOut->newLine();
2410 mxOut->incIndent();
2413 /* Start element: remember it and its text, to be able to print the
2414 matching end element on the same line in the next iteration. */
2415 if( bStartElem )
2417 aOldStartElem.append( aElem + aText );
2419 else
2421 /* End element: if a start element has been remembered in the
2422 previous iteration, write it out here untrimmed, to show
2423 all whitespace in the element text, and without trailing
2424 line break. Code below will add the end element right after
2425 it. Otherwise, return to previous indentation level. */
2426 if( bEndElem )
2428 if( aOldStartElem.getLength() == 0 )
2429 mxOut->decIndent();
2430 else
2431 mxOut->writeString( aOldStartElem.makeStringAndClear() );
2434 /* Write the element. Write following element text in a new
2435 line, but only, if it does not contain of white space
2436 entirely. */
2437 mxOut->writeString( aElem );
2438 mxOut->newLine();
2439 if( !o3tl::trim(aText).empty() )
2441 mxOut->writeString( aText );
2442 mxOut->newLine();
2449 void RecordObjectBase::construct( const ObjectBase& rParent,
2450 const BinaryInputStreamRef& rxBaseStrm, const OUString& rSysFileName,
2451 const BinaryInputStreamRef& rxRecStrm, const String& rRecNames, const String& rSimpleRecs )
2453 InputObjectBase::construct( rParent, rxRecStrm, rSysFileName );
2454 constructRecObjBase( rxBaseStrm, rRecNames, rSimpleRecs );
2457 bool RecordObjectBase::implIsValid() const
2459 return mxBaseStrm && InputObjectBase::implIsValid();
2462 void RecordObjectBase::implDump()
2464 NameListRef xRecNames = maRecNames.getNameList( cfg() );
2465 ItemFormatMap aSimpleRecs( maSimpleRecs.getNameList( cfg() ) );
2467 while( implStartRecord( *mxBaseStrm, mnRecPos, mnRecId, mnRecSize ) )
2469 // record header
2470 mxOut->emptyLine();
2471 writeHeader();
2472 implWriteExtHeader();
2473 IndentGuard aIndGuard( mxOut );
2474 sal_Int64 nRecPos = mxStrm->tell();
2476 // record body
2477 if( !mbBinaryOnly && cfg().hasName( xRecNames, mnRecId ) )
2479 ::std::map< sal_Int64, ItemFormat >::const_iterator aIt = aSimpleRecs.find( mnRecId );
2480 if( aIt != aSimpleRecs.end() )
2481 dumpItem( aIt->second );
2482 else
2483 implDumpRecordBody();
2486 // remaining undumped data
2487 if( !mxStrm->isEof() && (mxStrm->tell() == nRecPos) )
2488 dumpRawBinary( mnRecSize, false );
2489 else
2490 dumpRemainingTo( nRecPos + mnRecSize );
2494 void RecordObjectBase::implWriteExtHeader()
2498 void RecordObjectBase::implDumpRecordBody()
2502 void RecordObjectBase::constructRecObjBase( const BinaryInputStreamRef& rxBaseStrm, const String& rRecNames, const String& rSimpleRecs )
2504 mxBaseStrm = rxBaseStrm;
2505 maRecNames = rRecNames;
2506 maSimpleRecs = rSimpleRecs;
2507 mnRecPos = mnRecId = mnRecSize = 0;
2508 mbBinaryOnly = false;
2509 if( InputObjectBase::implIsValid() )
2510 mbShowRecPos = cfg().getBoolOption( "show-record-position", true );
2513 void RecordObjectBase::writeHeader()
2515 MultiItemsGuard aMultiGuard( mxOut );
2516 writeEmptyItem( "REC" );
2517 if( mbShowRecPos && mxBaseStrm->isSeekable() )
2518 writeShortHexItem( "pos", mnRecPos, "CONV-DEC" );
2519 writeShortHexItem( "size", mnRecSize, "CONV-DEC" );
2520 ItemGuard aItem( mxOut, "id" );
2521 mxOut->writeShortHex( mnRecId );
2522 addNameToItem( mnRecId, "CONV-DEC" );
2523 addNameToItem( mnRecId, maRecNames );
2526 void SequenceRecordObjectBase::construct( const ObjectBase& rParent,
2527 const BinaryInputStreamRef& rxBaseStrm, const OUString& rSysFileName,
2528 const String& rRecNames, const String& rSimpleRecs )
2530 BinaryInputStreamRef xRecStrm( std::make_shared<SequenceInputStream>( *mxRecData ) );
2531 RecordObjectBase::construct( rParent, rxBaseStrm, rSysFileName, xRecStrm, rRecNames, rSimpleRecs );
2534 bool SequenceRecordObjectBase::implStartRecord( BinaryInputStream& rBaseStrm, sal_Int64& ornRecPos, sal_Int64& ornRecId, sal_Int64& ornRecSize )
2536 bool bValid = true;
2537 if( rBaseStrm.isSeekable() )
2539 ornRecPos = rBaseStrm.tell();
2540 // do not try to overread seekable streams, may cause assertions
2541 bValid = ornRecPos < rBaseStrm.size();
2544 // read the record header
2545 if( bValid )
2546 bValid = implReadRecordHeader( rBaseStrm, ornRecId, ornRecSize ) && !rBaseStrm.isEof() && (0 <= ornRecSize) && (ornRecSize <= 0x00100000);
2548 // read record contents into data sequence
2549 if( bValid )
2551 sal_Int32 nRecSize = static_cast< sal_Int32 >( ornRecSize );
2552 mxRecData->realloc( nRecSize );
2553 bValid = (nRecSize == 0) || (rBaseStrm.readData( *mxRecData, nRecSize ) == nRecSize);
2554 mxStrm->seekToStart();
2556 return bValid;
2559 DumperBase::~DumperBase()
2563 bool DumperBase::isImportEnabled() const
2565 return !isValid() || cfg().isImportEnabled();
2568 void DumperBase::construct( const ConfigRef& rxConfig )
2570 if( isValid( rxConfig ) && rxConfig->isDumperEnabled() )
2571 ObjectBase::construct( rxConfig );
2574 } // namespace oox
2576 #endif
2578 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */