Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / oox / source / dump / dumperbase.cxx
blob8e8aad365c7f073fdbdbc025e72d7185560e2a16
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::beans;
44 using namespace ::com::sun::star::io;
45 using namespace ::com::sun::star::lang;
46 using namespace ::com::sun::star::ucb;
47 using namespace ::com::sun::star::uno;
49 using ::oox::core::FilterBase;
51 namespace {
53 const sal_Unicode OOX_DUMP_BOM = 0xFEFF;
54 const sal_Int32 OOX_DUMP_MAXSTRLEN = 80;
55 const sal_Int32 OOX_DUMP_INDENT = 2;
56 const sal_Unicode OOX_DUMP_BINDOT = '.';
57 const sal_Unicode OOX_DUMP_CFG_LISTSEP = ',';
58 const sal_Unicode OOX_DUMP_CFG_QUOTE = '\'';
59 const sal_Unicode OOX_DUMP_LF = '\n';
60 const sal_Unicode OOX_DUMP_ITEMSEP = '=';
61 const sal_Int32 OOX_DUMP_BYTESPERLINE = 16;
62 const sal_Int64 OOX_DUMP_MAXARRAY = 16;
64 } // namespace
66 // file names -----------------------------------------------------------------
68 OUString InputOutputHelper::convertFileNameToUrl( const OUString& rFileName )
70 OUString aFileUrl;
71 if( ::osl::FileBase::getFileURLFromSystemPath( rFileName, aFileUrl ) == ::osl::FileBase::E_None )
72 return aFileUrl;
73 return OUString();
76 sal_Int32 InputOutputHelper::getFileNamePos( std::u16string_view rFileUrl )
78 size_t nSepPos = rFileUrl.find( '/' );
79 return (nSepPos == std::u16string_view::npos) ? 0 : (nSepPos + 1);
82 std::u16string_view InputOutputHelper::getFileNameExtension( std::u16string_view rFileUrl )
84 sal_Int32 nNamePos = getFileNamePos( rFileUrl );
85 size_t nExtPos = rFileUrl.rfind( '.' );
86 if( nExtPos != std::u16string_view::npos && static_cast<sal_Int32>(nExtPos) >= nNamePos )
87 return rFileUrl.substr( nExtPos + 1 );
88 return std::u16string_view();
91 // input streams --------------------------------------------------------------
93 Reference< XInputStream > InputOutputHelper::openInputStream(
94 const Reference< XComponentContext >& rxContext, const OUString& rFileName )
96 Reference< XInputStream > xInStrm;
97 if( rxContext.is() ) try
99 Reference<XSimpleFileAccess3> xFileAccess(SimpleFileAccess::create(rxContext));
100 xInStrm = xFileAccess->openFileRead( rFileName );
102 catch( Exception& )
105 return xInStrm;
108 // output streams -------------------------------------------------------------
110 Reference< XOutputStream > InputOutputHelper::openOutputStream(
111 const Reference< XComponentContext >& rxContext, const OUString& rFileName )
113 Reference< XOutputStream > xOutStrm;
114 if( rxContext.is() ) try
116 Reference<XSimpleFileAccess3> xFileAccess(SimpleFileAccess::create(rxContext));
117 xOutStrm = xFileAccess->openFileWrite( rFileName );
119 catch( Exception& )
122 return xOutStrm;
125 Reference< XTextOutputStream2 > InputOutputHelper::openTextOutputStream(
126 const Reference< XComponentContext >& rxContext, const Reference< XOutputStream >& rxOutStrm, rtl_TextEncoding eTextEnc )
128 Reference< XTextOutputStream2 > xTextOutStrm;
129 const char* pcCharset = rtl_getMimeCharsetFromTextEncoding( eTextEnc );
130 if( rxContext.is() && rxOutStrm.is() && pcCharset ) try
132 xTextOutStrm = TextOutputStream::create(rxContext);
133 xTextOutStrm->setOutputStream( rxOutStrm );
134 xTextOutStrm->setEncoding( OUString::createFromAscii( pcCharset ) );
136 catch( Exception& )
139 return xTextOutStrm;
142 Reference< XTextOutputStream2 > InputOutputHelper::openTextOutputStream(
143 const Reference< XComponentContext >& rxContext, const OUString& rFileName, rtl_TextEncoding eTextEnc )
145 return openTextOutputStream( rxContext, openOutputStream( rxContext, rFileName ), eTextEnc );
148 ItemFormat::ItemFormat() :
149 meDataType( DATATYPE_VOID ),
150 meFmtType( FORMATTYPE_NONE )
154 void ItemFormat::set( DataType eDataType, FormatType eFmtType, const OUString& rItemName )
156 meDataType = eDataType;
157 meFmtType = eFmtType;
158 maItemName = rItemName;
159 maListName.clear();
162 OUStringVector::const_iterator ItemFormat::parse( const OUStringVector& rFormatVec )
164 set( DATATYPE_VOID, FORMATTYPE_NONE, OUString() );
166 OUStringVector::const_iterator aIt = rFormatVec.begin(), aEnd = rFormatVec.end();
167 OUString aDataType, aFmtType;
168 if( aIt != aEnd ) aDataType = *aIt++;
169 if( aIt != aEnd ) aFmtType = *aIt++;
170 if( aIt != aEnd ) maItemName = *aIt++;
171 if( aIt != aEnd ) maListName = *aIt++;
173 meDataType = StringHelper::convertToDataType( aDataType );
174 meFmtType = StringHelper::convertToFormatType( aFmtType );
176 if( meFmtType == FORMATTYPE_NONE )
178 if ( aFmtType == "unused" )
179 set( meDataType, FORMATTYPE_HEX, OOX_DUMP_UNUSED );
180 else if ( aFmtType == "unknown" )
181 set( meDataType, FORMATTYPE_HEX, OOX_DUMP_UNKNOWN );
184 return aIt;
187 OUStringVector ItemFormat::parse( std::u16string_view rFormatStr )
189 OUStringVector aFormatVec;
190 StringHelper::convertStringToStringList( aFormatVec, rFormatStr, false );
191 OUStringVector::const_iterator aIt = parse( aFormatVec );
192 return OUStringVector( aIt, const_cast< const OUStringVector& >( aFormatVec ).end() );
195 // append string to string ----------------------------------------------------
197 void StringHelper::appendChar( OUStringBuffer& rStr, sal_Unicode cChar, sal_Int32 nCount )
199 for( sal_Int32 nIndex = 0; nIndex < nCount; ++nIndex )
200 rStr.append( cChar );
203 void StringHelper::appendString( OUStringBuffer& rStr, std::u16string_view rData, sal_Int32 nWidth, sal_Unicode cFill )
205 appendChar( rStr, cFill, nWidth - rData.size() );
206 rStr.append( rData );
209 // append decimal -------------------------------------------------------------
211 void StringHelper::appendDec( OUStringBuffer& rStr, sal_uInt8 nData, sal_Int32 nWidth, sal_Unicode cFill )
213 appendString( rStr, OUString::number( nData ), nWidth, cFill );
216 void StringHelper::appendDec( OUStringBuffer& rStr, sal_Int8 nData, sal_Int32 nWidth, sal_Unicode cFill )
218 appendString( rStr, OUString::number( nData ), nWidth, cFill );
221 void StringHelper::appendDec( OUStringBuffer& rStr, sal_uInt16 nData, sal_Int32 nWidth, sal_Unicode cFill )
223 appendString( rStr, OUString::number( nData ), nWidth, cFill );
226 void StringHelper::appendDec( OUStringBuffer& rStr, sal_Int16 nData, sal_Int32 nWidth, sal_Unicode cFill )
228 appendString( rStr, OUString::number( nData ), nWidth, cFill );
231 void StringHelper::appendDec( OUStringBuffer& rStr, sal_uInt32 nData, sal_Int32 nWidth, sal_Unicode cFill )
233 appendString( rStr, OUString::number( nData ), nWidth, cFill );
236 void StringHelper::appendDec( OUStringBuffer& rStr, sal_Int32 nData, sal_Int32 nWidth, sal_Unicode cFill )
238 appendString( rStr, OUString::number( nData ), nWidth, cFill );
241 void StringHelper::appendDec( OUStringBuffer& rStr, sal_uInt64 nData, sal_Int32 nWidth, sal_Unicode cFill )
243 /* Values greater than biggest signed 64bit integer will change to
244 negative when converting to sal_Int64. Therefore, the trailing digit
245 will be written separately. */
246 OUStringBuffer aBuffer;
247 if( nData > 9 )
248 aBuffer.append( static_cast<sal_Int64>(nData / 10 ) );
249 aBuffer.append( static_cast< sal_Unicode >( '0' + (nData % 10) ) );
250 appendString( rStr, aBuffer.makeStringAndClear(), nWidth, cFill );
253 void StringHelper::appendDec( OUStringBuffer& rStr, sal_Int64 nData, sal_Int32 nWidth, sal_Unicode cFill )
255 appendString( rStr, OUString::number( nData ), nWidth, cFill );
258 void StringHelper::appendDec( OUStringBuffer& rStr, double fData, sal_Int32 nWidth, sal_Unicode cFill )
260 appendString( rStr, ::rtl::math::doubleToUString( fData, rtl_math_StringFormat_G, 15, '.', true ), nWidth, cFill );
263 // append hexadecimal ---------------------------------------------------------
265 void StringHelper::appendHex( OUStringBuffer& rStr, sal_uInt8 nData, bool bPrefix )
267 static const sal_Unicode spcHexDigits[] = { '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F' };
268 if( bPrefix )
269 rStr.append( "0x" );
270 rStr.append( OUStringChar(spcHexDigits[ (nData >> 4) & 0x0F ] ) + OUStringChar( spcHexDigits[ nData & 0x0F ] ) );
273 void StringHelper::appendHex( OUStringBuffer& rStr, sal_Int8 nData, bool bPrefix )
275 appendHex( rStr, static_cast< sal_uInt8 >( nData ), bPrefix );
278 void StringHelper::appendHex( OUStringBuffer& rStr, sal_uInt16 nData, bool bPrefix )
280 appendHex( rStr, static_cast< sal_uInt8 >( nData >> 8 ), bPrefix );
281 appendHex( rStr, static_cast< sal_uInt8 >( nData ), false );
284 void StringHelper::appendHex( OUStringBuffer& rStr, sal_Int16 nData, bool bPrefix )
286 appendHex( rStr, static_cast< sal_uInt16 >( nData ), bPrefix );
289 void StringHelper::appendHex( OUStringBuffer& rStr, sal_uInt32 nData, bool bPrefix )
291 appendHex( rStr, static_cast< sal_uInt16 >( nData >> 16 ), bPrefix );
292 appendHex( rStr, static_cast< sal_uInt16 >( nData ), false );
295 void StringHelper::appendHex( OUStringBuffer& rStr, sal_Int32 nData, bool bPrefix )
297 appendHex( rStr, static_cast< sal_uInt32 >( nData ), bPrefix );
300 void StringHelper::appendHex( OUStringBuffer& rStr, sal_uInt64 nData, bool bPrefix )
302 appendHex( rStr, static_cast< sal_uInt32 >( nData >> 32 ), bPrefix );
303 appendHex( rStr, static_cast< sal_uInt32 >( nData ), false );
306 void StringHelper::appendHex( OUStringBuffer& rStr, sal_Int64 nData, bool bPrefix )
308 appendHex( rStr, static_cast< sal_uInt64 >( nData ), bPrefix );
311 static sal_uInt64
312 lcl_ConvertDouble(double const f)
314 sal_uInt64 i = sal_uInt64();
315 for (size_t j = 0; j < sizeof(double); ++j)
316 { // hopefully both endian independent and strict aliasing safe
317 reinterpret_cast<char *>(&i)[j] = reinterpret_cast<char const *>(&f)[j];
319 return i;
322 void StringHelper::appendHex( OUStringBuffer& rStr, double fData, bool bPrefix )
324 appendHex( rStr, lcl_ConvertDouble(fData), bPrefix );
327 // append shortened hexadecimal -----------------------------------------------
329 void StringHelper::appendShortHex( OUStringBuffer& rStr, sal_uInt8 nData, bool bPrefix )
331 appendHex( rStr, nData, bPrefix );
334 void StringHelper::appendShortHex( OUStringBuffer& rStr, sal_Int8 nData, bool bPrefix )
336 appendHex( rStr, nData, bPrefix );
339 void StringHelper::appendShortHex( OUStringBuffer& rStr, sal_uInt16 nData, bool bPrefix )
341 if( nData > SAL_MAX_UINT8 )
342 appendHex( rStr, nData, bPrefix );
343 else
344 appendHex( rStr, static_cast< sal_uInt8 >( nData ), bPrefix );
347 void StringHelper::appendShortHex( OUStringBuffer& rStr, sal_Int16 nData, bool bPrefix )
349 appendShortHex( rStr, static_cast< sal_uInt16 >( nData ), bPrefix );
352 void StringHelper::appendShortHex( OUStringBuffer& rStr, sal_uInt32 nData, bool bPrefix )
354 if( nData > SAL_MAX_UINT16 )
355 appendHex( rStr, nData, bPrefix );
356 else
357 appendShortHex( rStr, static_cast< sal_uInt16 >( nData ), bPrefix );
360 void StringHelper::appendShortHex( OUStringBuffer& rStr, sal_Int32 nData, bool bPrefix )
362 appendShortHex( rStr, static_cast< sal_uInt32 >( nData ), bPrefix );
365 void StringHelper::appendShortHex( OUStringBuffer& rStr, sal_uInt64 nData, bool bPrefix )
367 if( nData > SAL_MAX_UINT32 )
368 appendHex( rStr, nData, bPrefix );
369 else
370 appendShortHex( rStr, static_cast< sal_uInt32 >( nData ), bPrefix );
373 void StringHelper::appendShortHex( OUStringBuffer& rStr, sal_Int64 nData, bool bPrefix )
375 appendShortHex( rStr, static_cast< sal_uInt64 >( nData ), bPrefix );
378 void StringHelper::appendShortHex( OUStringBuffer& rStr, double fData, bool bPrefix )
380 appendHex( rStr, fData, bPrefix );
383 // append binary --------------------------------------------------------------
385 void StringHelper::appendBin( OUStringBuffer& rStr, sal_uInt8 nData, bool bDots )
387 for( sal_uInt8 nMask = 0x80; nMask != 0; (nMask >>= 1) &= 0x7F )
389 rStr.append( static_cast< sal_Unicode >( (nData & nMask) ? '1' : '0' ) );
390 if( bDots && (nMask == 0x10) )
391 rStr.append( OOX_DUMP_BINDOT );
395 void StringHelper::appendBin( OUStringBuffer& rStr, sal_Int8 nData, bool bDots )
397 appendBin( rStr, static_cast< sal_uInt8 >( nData ), bDots );
400 void StringHelper::appendBin( OUStringBuffer& rStr, sal_uInt16 nData, bool bDots )
402 appendBin( rStr, static_cast< sal_uInt8 >( nData >> 8 ), bDots );
403 if( bDots )
404 rStr.append( OOX_DUMP_BINDOT );
405 appendBin( rStr, static_cast< sal_uInt8 >( nData ), bDots );
408 void StringHelper::appendBin( OUStringBuffer& rStr, sal_Int16 nData, bool bDots )
410 appendBin( rStr, static_cast< sal_uInt16 >( nData ), bDots );
413 void StringHelper::appendBin( OUStringBuffer& rStr, sal_uInt32 nData, bool bDots )
415 appendBin( rStr, static_cast< sal_uInt16 >( nData >> 16 ), bDots );
416 if( bDots )
417 rStr.append( OOX_DUMP_BINDOT );
418 appendBin( rStr, static_cast< sal_uInt16 >( nData ), bDots );
421 void StringHelper::appendBin( OUStringBuffer& rStr, sal_Int32 nData, bool bDots )
423 appendBin( rStr, static_cast< sal_uInt32 >( nData ), bDots );
426 void StringHelper::appendBin( OUStringBuffer& rStr, sal_uInt64 nData, bool bDots )
428 appendBin( rStr, static_cast< sal_uInt32 >( nData >> 32 ), bDots );
429 if( bDots )
430 rStr.append( OOX_DUMP_BINDOT );
431 appendBin( rStr, static_cast< sal_uInt32 >( nData ), bDots );
434 void StringHelper::appendBin( OUStringBuffer& rStr, sal_Int64 nData, bool bDots )
436 appendBin( rStr, static_cast< sal_uInt64 >( nData ), bDots );
439 void StringHelper::appendBin( OUStringBuffer& rStr, double fData, bool bDots )
441 appendBin( rStr, lcl_ConvertDouble(fData), bDots );
444 // append formatted value -----------------------------------------------------
446 void StringHelper::appendBool( OUStringBuffer& rStr, bool bData )
448 rStr.appendAscii( bData ? "true" : "false" );
451 // encoded text output --------------------------------------------------------
453 void StringHelper::appendCChar( OUStringBuffer& rStr, sal_Unicode cChar, bool bPrefix )
455 if( cChar > 0x00FF )
457 if( bPrefix )
458 rStr.append( "\\u" );
459 appendHex( rStr, static_cast< sal_uInt16 >( cChar ), false );
461 else
463 if( bPrefix )
464 rStr.append( "\\x" );
465 appendHex( rStr, static_cast< sal_uInt8 >( cChar ), false );
469 void StringHelper::appendEncChar( OUStringBuffer& rStr, sal_Unicode cChar, sal_Int32 nCount, bool bPrefix )
471 if( cChar < 0x0020 )
473 // C-style hex code
474 OUStringBuffer aCode;
475 appendCChar( aCode, cChar, bPrefix );
476 OUString aCodeStr = aCode.makeStringAndClear();
477 for( sal_Int32 nIdx = 0; nIdx < nCount; ++nIdx )
478 rStr.append( aCodeStr );
480 else
482 appendChar( rStr, cChar, nCount );
486 void StringHelper::appendEncString( OUStringBuffer& rStr, std::u16string_view rData, bool bPrefix )
488 size_t nBeg = 0;
489 size_t nIdx = 0;
490 size_t nEnd = rData.size();
491 while( nIdx < nEnd )
493 // find next character that needs encoding
494 while( (nIdx < nEnd) && (rData[ nIdx ] >= 0x20) ) ++nIdx;
495 // append portion
496 if( nBeg < nIdx )
498 if( (nBeg == 0) && (nIdx == nEnd) )
499 rStr.append( rData );
500 else
501 rStr.append( rData.substr(nBeg, nIdx - nBeg) );
503 // append characters to be encoded
504 while( (nIdx < nEnd) && (rData[ nIdx ] < 0x20) )
506 appendCChar( rStr, rData[ nIdx ], bPrefix );
507 ++nIdx;
509 // adjust limits
510 nBeg = nIdx;
514 // token list -----------------------------------------------------------------
516 void StringHelper::appendToken( OUStringBuffer& rStr, std::u16string_view rToken, sal_Unicode cSep )
518 if( (rStr.getLength() > 0) && (!rToken.empty()) )
519 rStr.append( cSep );
520 rStr.append( rToken );
523 void StringHelper::appendIndex( OUStringBuffer& rStr, sal_Int64 nIdx )
525 OUStringBuffer aToken;
526 appendDec( aToken, nIdx );
527 rStr.append( "[" + aToken + "]" );
530 std::u16string_view StringHelper::getToken( std::u16string_view rData, sal_Int32& rnPos, sal_Unicode cSep )
532 return trimSpaces( o3tl::getToken(rData, 0, cSep, rnPos ) );
535 void StringHelper::enclose( OUStringBuffer& rStr, sal_Unicode cOpen, sal_Unicode cClose )
537 rStr.insert( 0, cOpen ).append( cClose ? cClose : cOpen );
540 // string conversion ----------------------------------------------------------
542 namespace {
544 sal_Int32 lclIndexOf( std::u16string_view rStr, sal_Unicode cChar, sal_Int32 nStartPos )
546 size_t nIndex = rStr.find( cChar, nStartPos );
547 return (nIndex == std::u16string_view::npos) ? rStr.size() : nIndex;
550 OUString lclTrimQuotedStringList( std::u16string_view rStr )
552 OUStringBuffer aBuffer;
553 size_t nPos = 0;
554 size_t nLen = rStr.size();
555 while( nPos < nLen )
557 if( rStr[ nPos ] == OOX_DUMP_CFG_QUOTE )
559 // quoted string, skip leading quote character
560 ++nPos;
561 // process quoted text and embedded literal quote characters
562 OUStringBuffer aToken;
565 // seek to next quote character and add text portion to token buffer
566 size_t nEnd = lclIndexOf( rStr, OOX_DUMP_CFG_QUOTE, nPos );
567 aToken.append( rStr.substr(nPos, nEnd - nPos) );
568 // process literal quotes
569 while( (nEnd + 1 < nLen) && (rStr[ nEnd ] == OOX_DUMP_CFG_QUOTE) && (rStr[ nEnd + 1 ] == OOX_DUMP_CFG_QUOTE) )
571 aToken.append( OOX_DUMP_CFG_QUOTE );
572 nEnd += 2;
574 // nEnd is start of possible next text portion
575 nPos = nEnd;
577 while( (nPos < nLen) && (rStr[ nPos ] != OOX_DUMP_CFG_QUOTE) );
578 // add token, seek to list separator, ignore text following closing quote
579 aBuffer.append( aToken );
580 nPos = lclIndexOf( rStr, OOX_DUMP_CFG_LISTSEP, nPos );
581 if( nPos < nLen )
582 aBuffer.append( OOX_DUMP_LF );
583 // set current position behind list separator
584 ++nPos;
586 else
588 // find list separator, add token text to buffer
589 size_t nEnd = lclIndexOf( rStr, OOX_DUMP_CFG_LISTSEP, nPos );
590 aBuffer.append( rStr.substr(nPos, nEnd - nPos) );
591 if( nEnd < nLen )
592 aBuffer.append( OOX_DUMP_LF );
593 // set current position behind list separator
594 nPos = nEnd + 1;
598 return aBuffer.makeStringAndClear();
601 } // namespace
603 std::u16string_view StringHelper::trimSpaces( std::u16string_view rStr )
605 size_t nBeg = 0;
606 while( (nBeg < rStr.size()) && ((rStr[ nBeg ] == ' ') || (rStr[ nBeg ] == '\t')) )
607 ++nBeg;
608 size_t nEnd = rStr.size();
609 while( (nEnd > nBeg) && ((rStr[ nEnd - 1 ] == ' ') || (rStr[ nEnd - 1 ] == '\t')) )
610 --nEnd;
611 return rStr.substr( nBeg, nEnd - nBeg );
614 OUString StringHelper::trimTrailingNul( const OUString& rStr )
616 sal_Int32 nLastPos = rStr.getLength() - 1;
617 if( (nLastPos >= 0) && (rStr[ nLastPos ] == 0) )
618 return rStr.copy( 0, nLastPos );
619 return rStr;
622 OString StringHelper::convertToUtf8( std::u16string_view rStr )
624 return OUStringToOString( rStr, RTL_TEXTENCODING_UTF8 );
627 DataType StringHelper::convertToDataType( std::u16string_view rStr )
629 DataType eType = DATATYPE_VOID;
630 if ( rStr == u"int8" )
631 eType = DATATYPE_INT8;
632 else if ( rStr == u"uint8" )
633 eType = DATATYPE_UINT8;
634 else if ( rStr == u"int16" )
635 eType = DATATYPE_INT16;
636 else if ( rStr == u"uint16" )
637 eType = DATATYPE_UINT16;
638 else if ( rStr == u"int32" )
639 eType = DATATYPE_INT32;
640 else if ( rStr == u"uint32" )
641 eType = DATATYPE_UINT32;
642 else if ( rStr == u"int64" )
643 eType = DATATYPE_INT64;
644 else if ( rStr == u"uint64" )
645 eType = DATATYPE_UINT64;
646 else if ( rStr == u"float" )
647 eType = DATATYPE_FLOAT;
648 else if ( rStr == u"double" )
649 eType = DATATYPE_DOUBLE;
650 return eType;
653 FormatType StringHelper::convertToFormatType( std::u16string_view rStr )
655 FormatType eType = FORMATTYPE_NONE;
656 if ( rStr == u"dec" )
657 eType = FORMATTYPE_DEC;
658 else if ( rStr == u"hex" )
659 eType = FORMATTYPE_HEX;
660 else if ( rStr == u"shorthex" )
661 eType = FORMATTYPE_SHORTHEX;
662 else if ( rStr == u"bin" )
663 eType = FORMATTYPE_BIN;
664 else if ( rStr == u"fix" )
665 eType = FORMATTYPE_FIX;
666 else if ( rStr == u"bool" )
667 eType = FORMATTYPE_BOOL;
668 return eType;
671 bool StringHelper::convertFromDec( sal_Int64& ornData, std::u16string_view rData )
673 size_t nPos = 0;
674 size_t nLen = rData.size();
675 bool bNeg = false;
676 if( (nLen > 0) && (rData[ 0 ] == '-') )
678 bNeg = true;
679 ++nPos;
681 ornData = 0;
682 for( ; nPos < nLen; ++nPos )
684 sal_Unicode cChar = rData[ nPos ];
685 if( (cChar < '0') || (cChar > '9') )
686 return false;
687 ornData = (ornData * 10) + (cChar - '0');
689 if( bNeg )
690 ornData *= -1;
691 return true;
694 bool StringHelper::convertFromHex( sal_Int64& ornData, std::u16string_view rData )
696 ornData = 0;
697 for( size_t nPos = 0, nLen = rData.size(); nPos < nLen; ++nPos )
699 sal_Unicode cChar = rData[ nPos ];
700 if( ('0' <= cChar) && (cChar <= '9') )
701 cChar -= '0';
702 else if( ('A' <= cChar) && (cChar <= 'F') )
703 cChar -= ('A' - 10);
704 else if( ('a' <= cChar) && (cChar <= 'f') )
705 cChar -= ('a' - 10);
706 else
707 return false;
708 ornData = (ornData << 4) + cChar;
710 return true;
713 bool StringHelper::convertStringToInt( sal_Int64& ornData, std::u16string_view rData )
715 if( (rData.size() > 2) && (rData[ 0 ] == '0') && ((rData[ 1 ] == 'X') || (rData[ 1 ] == 'x')) )
716 return convertFromHex( ornData, rData.substr( 2 ) );
717 return convertFromDec( ornData, rData );
720 bool StringHelper::convertStringToDouble( double& orfData, std::u16string_view rData )
722 rtl_math_ConversionStatus eStatus = rtl_math_ConversionStatus_Ok;
723 sal_Int32 nSize = 0;
724 sal_Unicode const * pBegin = rData.data();
725 sal_Unicode const * pEnd;
726 orfData = rtl_math_uStringToDouble(pBegin,
727 pBegin + rData.size(),
728 '.', '\0',
729 &eStatus, &pEnd);
730 nSize = static_cast<sal_Int32>(pEnd - pBegin);
731 return (eStatus == rtl_math_ConversionStatus_Ok) && (nSize == static_cast<sal_Int32>(rData.size()));
734 bool StringHelper::convertStringToBool( std::u16string_view rData )
736 if ( rData == u"true" )
737 return true;
738 if ( rData == u"false" )
739 return false;
740 sal_Int64 nData;
741 return convertStringToInt( nData, rData ) && (nData != 0);
744 OUStringPair StringHelper::convertStringToPair( const OUString& rString, sal_Unicode cSep )
746 OUStringPair aPair;
747 if( !rString.isEmpty() )
749 sal_Int32 nEqPos = rString.indexOf( cSep );
750 if( nEqPos < 0 )
752 aPair.first = rString;
754 else
756 aPair.first = StringHelper::trimSpaces( rString.subView( 0, nEqPos ) );
757 aPair.second = StringHelper::trimSpaces( rString.subView( nEqPos + 1 ) );
760 return aPair;
763 void StringHelper::convertStringToStringList( OUStringVector& orVec, std::u16string_view rData, bool bIgnoreEmpty )
765 orVec.clear();
766 OUString aUnquotedData = lclTrimQuotedStringList( rData );
767 sal_Int32 nPos = 0;
768 sal_Int32 nLen = aUnquotedData.getLength();
769 while( (0 <= nPos) && (nPos < nLen) )
771 std::u16string_view aToken = getToken( aUnquotedData, nPos, OOX_DUMP_LF );
772 if( !bIgnoreEmpty || !aToken.empty() )
773 orVec.push_back( OUString(aToken) );
777 void StringHelper::convertStringToIntList( Int64Vector& orVec, std::u16string_view rData, bool bIgnoreEmpty )
779 orVec.clear();
780 OUString aUnquotedData = lclTrimQuotedStringList( rData );
781 sal_Int32 nPos = 0;
782 sal_Int32 nLen = aUnquotedData.getLength();
783 sal_Int64 nData;
784 while( (0 <= nPos) && (nPos < nLen) )
786 bool bOk = convertStringToInt( nData, getToken( aUnquotedData, nPos, OOX_DUMP_LF ) );
787 if( !bIgnoreEmpty || bOk )
788 orVec.push_back( bOk ? nData : 0 );
792 Base::~Base()
796 ConfigItemBase::~ConfigItemBase()
800 void ConfigItemBase::readConfigBlock( TextInputStream& rStrm )
802 readConfigBlockContents( rStrm );
805 void ConfigItemBase::implProcessConfigItemStr(
806 TextInputStream& /*rStrm*/, const OUString& /*rKey*/, const OUString& /*rData*/ )
810 void ConfigItemBase::implProcessConfigItemInt(
811 TextInputStream& /*rStrm*/, sal_Int64 /*nKey*/, const OUString& /*rData*/ )
815 void ConfigItemBase::readConfigBlockContents( TextInputStream& rStrm )
817 bool bLoop = true;
818 while( bLoop && !rStrm.isEof() )
820 OUString aKey, aData;
821 switch( readConfigLine( rStrm, aKey, aData ) )
823 case LINETYPE_DATA:
824 processConfigItem( rStrm, aKey, aData );
825 break;
826 case LINETYPE_END:
827 bLoop = false;
828 break;
833 ConfigItemBase::LineType ConfigItemBase::readConfigLine(
834 TextInputStream& rStrm, OUString& orKey, OUString& orData )
836 OUString aLine;
837 while( !rStrm.isEof() && aLine.isEmpty() )
839 aLine = rStrm.readLine();
840 if( !aLine.isEmpty() && (aLine[ 0 ] == OOX_DUMP_BOM) )
841 aLine = aLine.copy( 1 );
842 aLine = StringHelper::trimSpaces( aLine );
843 if( !aLine.isEmpty() )
845 // ignore comments (starting with hash or semicolon)
846 sal_Unicode cChar = aLine[ 0 ];
847 if( (cChar == '#') || (cChar == ';') )
848 aLine.clear();
852 OUStringPair aPair = StringHelper::convertStringToPair( aLine );
853 orKey = aPair.first;
854 orData = aPair.second;
855 return ( !orKey.isEmpty() && (!orData.isEmpty() || orKey != "end" )) ?
856 LINETYPE_DATA : LINETYPE_END;
859 void ConfigItemBase::processConfigItem(
860 TextInputStream& rStrm, const OUString& rKey, const OUString& rData )
862 sal_Int64 nKey;
863 if( StringHelper::convertStringToInt( nKey, rKey ) )
864 implProcessConfigItemInt( rStrm, nKey, rData );
865 else
866 implProcessConfigItemStr( rStrm, rKey, rData );
869 NameListBase::~NameListBase()
873 void NameListBase::setName( sal_Int64 nKey, const String& rName )
875 implSetName( nKey, rName );
878 void NameListBase::includeList( const NameListRef& rxList )
880 if( rxList )
882 for (auto const& elem : *rxList)
883 maMap[ elem.first ] = elem.second;
884 implIncludeList( *rxList );
888 bool NameListBase::implIsValid() const
890 return true;
893 void NameListBase::implProcessConfigItemStr(
894 TextInputStream& rStrm, const OUString& rKey, const OUString& rData )
896 if ( rKey == "include" )
897 include( rData );
898 else if ( rKey == "exclude" )
899 exclude( rData );
900 else
901 ConfigItemBase::implProcessConfigItemStr( rStrm, rKey, rData );
904 void NameListBase::implProcessConfigItemInt(
905 TextInputStream& /*rStrm*/, sal_Int64 nKey, const OUString& rData )
907 implSetName( nKey, rData );
910 void NameListBase::insertRawName( sal_Int64 nKey, const OUString& rName )
912 maMap[ nKey ] = rName;
915 const OUString* NameListBase::findRawName( sal_Int64 nKey ) const
917 const_iterator aIt = maMap.find( nKey );
918 return (aIt == end()) ? nullptr : &aIt->second;
921 void NameListBase::include( std::u16string_view rListKeys )
923 OUStringVector aVec;
924 StringHelper::convertStringToStringList( aVec, rListKeys, true );
925 for (auto const& elem : aVec)
926 includeList( mrCfgData.getNameList(elem) );
929 void NameListBase::exclude( std::u16string_view rKeys )
931 Int64Vector aVec;
932 StringHelper::convertStringToIntList( aVec, rKeys, true );
933 for (auto const& elem : aVec)
934 maMap.erase(elem);
937 void ItemFormatMap::insertFormats( const NameListRef& rxNameList )
939 if( Base::isValid( rxNameList ) )
941 for (auto const& elemName : *rxNameList)
942 maMap[ elemName.first ].parse( elemName.second );
946 ConstList::ConstList( const SharedConfigData& rCfgData ) :
947 NameListBase( rCfgData ),
948 maDefName( OOX_DUMP_ERR_NONAME ),
949 mbQuoteNames( false )
953 void ConstList::implProcessConfigItemStr(
954 TextInputStream& rStrm, const OUString& rKey, const OUString& rData )
956 if ( rKey == "default" )
957 maDefName = rData; // Sets a default name for unknown keys.
958 else if ( rKey == "quote-names" )
959 setQuoteNames( StringHelper::convertStringToBool( rData ) );
960 else
961 NameListBase::implProcessConfigItemStr( rStrm, rKey, rData );
964 void ConstList::implSetName( sal_Int64 nKey, const OUString& rName )
966 insertRawName( nKey, rName );
969 OUString ConstList::implGetName( const Config& /*rCfg*/, sal_Int64 nKey ) const
971 const OUString* pName = findRawName( nKey );
972 OUString aName = pName ? *pName : maDefName;
973 if( mbQuoteNames )
975 OUStringBuffer aBuffer( aName );
976 StringHelper::enclose( aBuffer, OOX_DUMP_STRQUOTE );
977 aName = aBuffer.makeStringAndClear();
979 return aName;
982 OUString ConstList::implGetNameDbl( const Config& /*rCfg*/, double /*fValue*/ ) const
984 return OUString();
987 void ConstList::implIncludeList( const NameListBase& rList )
989 if( const ConstList* pConstList = dynamic_cast< const ConstList* >( &rList ) )
991 maDefName = pConstList->maDefName;
992 mbQuoteNames = pConstList->mbQuoteNames;
996 MultiList::MultiList( const SharedConfigData& rCfgData ) :
997 ConstList( rCfgData ),
998 mbIgnoreEmpty( true )
1002 void MultiList::setNamesFromVec( sal_Int64 nStartKey, const OUStringVector& rNames )
1004 sal_Int64 nKey = nStartKey;
1005 for (auto const& name : rNames)
1007 if( !mbIgnoreEmpty || !name.isEmpty() )
1008 insertRawName( nKey, name);
1009 ++nKey;
1013 void MultiList::implProcessConfigItemStr(
1014 TextInputStream& rStrm, const OUString& rKey, const OUString& rData )
1016 if ( rKey == "ignore-empty" )
1017 mbIgnoreEmpty = StringHelper::convertStringToBool( rData );
1018 else
1019 ConstList::implProcessConfigItemStr( rStrm, rKey, rData );
1022 void MultiList::implSetName( sal_Int64 nKey, const OUString& rName )
1024 OUStringVector aNames;
1025 StringHelper::convertStringToStringList( aNames, rName, false );
1026 setNamesFromVec( nKey, aNames );
1029 FlagsList::FlagsList( const SharedConfigData& rCfgData ) :
1030 NameListBase( rCfgData ),
1031 mnIgnore( 0 )
1035 void FlagsList::implProcessConfigItemStr(
1036 TextInputStream& rStrm, const OUString& rKey, const OUString& rData )
1038 if ( rKey == "ignore" )
1040 sal_Int64 nIgnore;
1041 if( StringHelper::convertStringToInt( nIgnore, rData ) )
1042 setIgnoreFlags( nIgnore );
1044 else
1046 NameListBase::implProcessConfigItemStr( rStrm, rKey, rData );
1050 void FlagsList::implSetName( sal_Int64 nKey, const OUString& rName )
1052 if( (nKey != 0) && ((nKey & (nKey - 1)) == 0) ) // only a single bit set?
1053 insertRawName( nKey, rName );
1056 OUString FlagsList::implGetName( const Config& /*rCfg*/, sal_Int64 nKey ) const
1058 sal_Int64 nFound = mnIgnore;
1059 OUStringBuffer aName;
1060 // add known flags
1061 for( const_iterator aIt = begin(), aEnd = end(); aIt != aEnd; ++aIt )
1063 sal_Int64 nMask = aIt->first;
1064 setFlag( nFound, nMask );
1065 if( !getFlag( mnIgnore, nMask ) )
1067 const OUString& rFlagName = aIt->second;
1068 bool bOnOff = rFlagName.startsWith(":");
1069 bool bFlag = getFlag( nKey, nMask );
1070 if( bOnOff )
1072 StringHelper::appendToken( aName, rFlagName.subView( 1 ) );
1073 aName.appendAscii( bFlag ? ":on" : ":off" );
1075 else
1077 bool bNegated = rFlagName.startsWith("!");
1078 sal_Int32 nBothSep = bNegated ? rFlagName.indexOf( '!', 1 ) : -1;
1079 if( bFlag )
1081 if( !bNegated )
1082 StringHelper::appendToken( aName, rFlagName );
1083 else if( nBothSep > 0 )
1084 StringHelper::appendToken( aName, rFlagName.subView( nBothSep + 1 ) );
1086 else if( bNegated )
1088 if( nBothSep > 0 )
1089 StringHelper::appendToken( aName, rFlagName.subView( 1, nBothSep - 1 ) );
1090 else
1091 StringHelper::appendToken( aName, rFlagName.subView( 1 ) );
1096 // add unknown flags
1097 setFlag( nKey, nFound, false );
1098 if( nKey != 0 )
1100 OUStringBuffer aUnknown( OUString::Concat(OOX_DUMP_UNKNOWN) + OUStringChar(OOX_DUMP_ITEMSEP) );
1101 StringHelper::appendShortHex( aUnknown, nKey );
1102 StringHelper::enclose( aUnknown, '(', ')' );
1103 StringHelper::appendToken( aName, aUnknown );
1105 return aName.makeStringAndClear();
1108 OUString FlagsList::implGetNameDbl( const Config& /*rCfg*/, double /*fValue*/ ) const
1110 return OUString();
1113 void FlagsList::implIncludeList( const NameListBase& rList )
1115 if( const FlagsList* pFlagsList = dynamic_cast< const FlagsList* >( &rList ) )
1116 mnIgnore = pFlagsList->mnIgnore;
1119 bool CombiList::ExtItemFormatKey::operator<( const ExtItemFormatKey& rRight ) const
1121 return (mnKey < rRight.mnKey) || ((mnKey == rRight.mnKey) && (maFilter < rRight.maFilter));
1124 CombiList::CombiList( const SharedConfigData& rCfgData ) :
1125 FlagsList( rCfgData )
1129 void CombiList::implSetName( sal_Int64 nKey, const OUString& rName )
1131 if( (nKey & (nKey - 1)) != 0 ) // more than a single bit set?
1133 ::std::set< ExtItemFormatKey > aItemKeys;
1134 ExtItemFormat aItemFmt;
1135 OUStringVector aRemain = aItemFmt.parse( rName );
1136 for (auto const& elemRemain : aRemain)
1138 OUStringPair aPair = StringHelper::convertStringToPair(elemRemain);
1139 if ( aPair.first == "noshift" )
1141 aItemFmt.mbShiftValue = StringHelper::convertStringToBool( aPair.second );
1143 else if ( aPair.first == "filter" )
1145 OUStringPair aFilter = StringHelper::convertStringToPair( aPair.second, '~' );
1146 ExtItemFormatKey aKey( nKey );
1147 if( !aFilter.first.isEmpty() && StringHelper::convertStringToInt( aKey.maFilter.first, aFilter.first ) &&
1148 !aFilter.second.isEmpty() && StringHelper::convertStringToInt( aKey.maFilter.second, aFilter.second ) )
1150 if( aKey.maFilter.first == 0 )
1151 aKey.maFilter.second = 0;
1152 aItemKeys.insert( aKey );
1156 if( aItemKeys.empty() )
1157 aItemKeys.insert( ExtItemFormatKey( nKey ) );
1158 for (auto const& itemKey : aItemKeys)
1159 maFmtMap[itemKey] = aItemFmt;
1161 else
1163 FlagsList::implSetName( nKey, rName );
1167 OUString CombiList::implGetName( const Config& rCfg, sal_Int64 nKey ) const
1169 sal_Int64 nFound = 0;
1170 OUStringBuffer aName;
1171 // add known flag fields
1172 for (auto const& fmt : maFmtMap)
1174 const ExtItemFormatKey& rMapKey = fmt.first;
1175 sal_Int64 nMask = rMapKey.mnKey;
1176 if( (nMask != 0) && ((nKey & rMapKey.maFilter.first) == rMapKey.maFilter.second) )
1178 const ExtItemFormat& rItemFmt = fmt.second;
1180 sal_uInt64 nUFlags = static_cast< sal_uInt64 >( nKey );
1181 sal_uInt64 nUMask = static_cast< sal_uInt64 >( nMask );
1182 if( rItemFmt.mbShiftValue )
1183 while( (nUMask & 1) == 0 ) { nUFlags >>= 1; nUMask >>= 1; }
1185 sal_uInt64 nUValue = nUFlags & nUMask;
1186 sal_Int64 nSValue = static_cast< sal_Int64 >( nUValue );
1187 if( getFlag< sal_uInt64 >( nUValue, (nUMask + 1) >> 1 ) )
1188 setFlag( nSValue, static_cast< sal_Int64 >( ~nUMask ) );
1190 OUStringBuffer aItem( rItemFmt.maItemName );
1191 OUStringBuffer aValue;
1192 switch( rItemFmt.meDataType )
1194 case DATATYPE_INT8: StringHelper::appendValue( aValue, static_cast< sal_Int8 >( nSValue ), rItemFmt.meFmtType ); break;
1195 case DATATYPE_UINT8: StringHelper::appendValue( aValue, static_cast< sal_uInt8 >( nUValue ), rItemFmt.meFmtType ); break;
1196 case DATATYPE_INT16: StringHelper::appendValue( aValue, static_cast< sal_Int16 >( nSValue ), rItemFmt.meFmtType ); break;
1197 case DATATYPE_UINT16: StringHelper::appendValue( aValue, static_cast< sal_uInt16 >( nUValue ), rItemFmt.meFmtType ); break;
1198 case DATATYPE_INT32: StringHelper::appendValue( aValue, static_cast< sal_Int32 >( nSValue ), rItemFmt.meFmtType ); break;
1199 case DATATYPE_UINT32: StringHelper::appendValue( aValue, static_cast< sal_uInt32 >( nUValue ), rItemFmt.meFmtType ); break;
1200 case DATATYPE_INT64: StringHelper::appendValue( aValue, nSValue, rItemFmt.meFmtType ); break;
1201 case DATATYPE_UINT64: StringHelper::appendValue( aValue, nUValue, rItemFmt.meFmtType ); break;
1202 case DATATYPE_FLOAT: StringHelper::appendValue( aValue, static_cast< float >( nSValue ), rItemFmt.meFmtType ); break;
1203 case DATATYPE_DOUBLE: StringHelper::appendValue( aValue, static_cast< double >( nSValue ), rItemFmt.meFmtType ); break;
1204 default:;
1206 StringHelper::appendToken( aItem, aValue, OOX_DUMP_ITEMSEP );
1207 if( !rItemFmt.maListName.isEmpty() )
1209 OUString aValueName = rCfg.getName( rItemFmt.maListName, static_cast< sal_Int64 >( nUValue ) );
1210 StringHelper::appendToken( aItem, aValueName, OOX_DUMP_ITEMSEP );
1212 StringHelper::enclose( aItem, '(', ')' );
1213 StringHelper::appendToken( aName, aItem );
1214 setFlag( nFound, nMask );
1217 setFlag( nKey, nFound, false );
1218 StringHelper::appendToken( aName, FlagsList::implGetName( rCfg, nKey ) );
1219 return aName.makeStringAndClear();
1222 void CombiList::implIncludeList( const NameListBase& rList )
1224 if( const CombiList* pCombiList = dynamic_cast< const CombiList* >( &rList ) )
1225 maFmtMap = pCombiList->maFmtMap;
1226 FlagsList::implIncludeList( rList );
1229 UnitConverter::UnitConverter( const SharedConfigData& rCfgData ) :
1230 NameListBase( rCfgData ),
1231 mfFactor( 1.0 )
1235 void UnitConverter::implSetName( sal_Int64 /*nKey*/, const OUString& /*rName*/ )
1237 // nothing to do
1240 OUString UnitConverter::implGetName( const Config& rCfg, sal_Int64 nKey ) const
1242 return implGetNameDbl( rCfg, static_cast< double >( nKey ) );
1245 OUString UnitConverter::implGetNameDbl( const Config& /*rCfg*/, double fValue ) const
1247 OUStringBuffer aValue;
1248 StringHelper::appendDec( aValue, mfFactor * fValue );
1249 aValue.append( maUnitName );
1250 return aValue.makeStringAndClear();
1253 void UnitConverter::implIncludeList( const NameListBase& /*rList*/ )
1257 const NameListRef & NameListWrapper::getNameList( const Config& rCfg ) const
1259 if (!mxList)
1260 mxList = rCfg.getNameList( maName );
1261 return mxList;
1264 SharedConfigData::SharedConfigData( const OUString& rFileName,
1265 const Reference< XComponentContext >& rxContext, StorageRef xRootStrg,
1266 OUString aSysFileName ) :
1267 mxContext( rxContext ),
1268 mxRootStrg(std::move( xRootStrg )),
1269 maSysFileName(std::move( aSysFileName )),
1270 mbLoaded( false )
1272 OUString aFileUrl = InputOutputHelper::convertFileNameToUrl( rFileName );
1273 if( !aFileUrl.isEmpty() )
1275 sal_Int32 nNamePos = InputOutputHelper::getFileNamePos( aFileUrl );
1276 maConfigPath = aFileUrl.copy( 0, nNamePos );
1277 mbLoaded = readConfigFile( aFileUrl );
1281 SharedConfigData::~SharedConfigData()
1285 const OUString* SharedConfigData::getOption( const OUString& rKey ) const
1287 ConfigDataMap::const_iterator aIt = maConfigData.find( rKey );
1288 return (aIt == maConfigData.end()) ? nullptr : &aIt->second;
1291 void SharedConfigData::setNameList( const OUString& rListName, const NameListRef& rxList )
1293 if( !rListName.isEmpty() )
1294 maNameLists[ rListName ] = rxList;
1297 void SharedConfigData::eraseNameList( const OUString& rListName )
1299 maNameLists.erase( rListName );
1302 NameListRef SharedConfigData::getNameList( const OUString& rListName ) const
1304 NameListRef xList;
1305 NameListMap::const_iterator aIt = maNameLists.find( rListName );
1306 if( aIt != maNameLists.end() )
1307 xList = aIt->second;
1308 return xList;
1311 bool SharedConfigData::implIsValid() const
1313 return mbLoaded && mxContext.is() && mxRootStrg && !maSysFileName.isEmpty();
1316 void SharedConfigData::implProcessConfigItemStr(
1317 TextInputStream& rStrm, const OUString& rKey, const OUString& rData )
1319 if ( rKey == "include-config-file" )
1320 readConfigFile( maConfigPath + rData );
1321 else if ( rKey == "constlist" )
1322 readNameList< ConstList >( rStrm, rData );
1323 else if ( rKey == "multilist" )
1324 readNameList< MultiList >( rStrm, rData );
1325 else if ( rKey == "flagslist" )
1326 readNameList< FlagsList >( rStrm, rData );
1327 else if ( rKey == "combilist" )
1328 readNameList< CombiList >( rStrm, rData );
1329 else if ( rKey == "shortlist" )
1330 createShortList( rData );
1331 else if ( rKey == "unitconverter" )
1332 createUnitConverter( rData );
1333 else
1334 maConfigData[ rKey ] = rData;
1337 bool SharedConfigData::readConfigFile( const OUString& rFileUrl )
1339 bool bLoaded = maConfigFiles.count( rFileUrl ) > 0;
1340 if( !bLoaded )
1342 Reference< XInputStream > xInStrm = InputOutputHelper::openInputStream( mxContext, rFileUrl );
1343 TextInputStream aTxtStrm( mxContext, xInStrm, RTL_TEXTENCODING_UTF8 );
1344 if( !aTxtStrm.isEof() )
1346 maConfigFiles.insert( rFileUrl );
1347 readConfigBlockContents( aTxtStrm );
1348 bLoaded = true;
1351 return bLoaded;
1354 void SharedConfigData::createShortList( std::u16string_view rData )
1356 OUStringVector aDataVec;
1357 StringHelper::convertStringToStringList( aDataVec, rData, false );
1358 if( aDataVec.size() < 3 )
1359 return;
1361 sal_Int64 nStartKey;
1362 if( StringHelper::convertStringToInt( nStartKey, aDataVec[ 1 ] ) )
1364 std::shared_ptr< MultiList > xList = createNameList< MultiList >( aDataVec[ 0 ] );
1365 if( xList )
1367 aDataVec.erase( aDataVec.begin(), aDataVec.begin() + 2 );
1368 xList->setNamesFromVec( nStartKey, aDataVec );
1373 void SharedConfigData::createUnitConverter( std::u16string_view rData )
1375 OUStringVector aDataVec;
1376 StringHelper::convertStringToStringList( aDataVec, rData, false );
1377 if( aDataVec.size() < 2 )
1378 return;
1380 OUString aFactor = aDataVec[ 1 ];
1381 bool bRecip = aFactor.startsWith("/");
1382 if( bRecip )
1383 aFactor = aFactor.copy( 1 );
1384 double fFactor;
1385 if( StringHelper::convertStringToDouble( fFactor, aFactor ) && (fFactor != 0.0) )
1387 std::shared_ptr< UnitConverter > xList = createNameList< UnitConverter >( aDataVec[ 0 ] );
1388 if( xList )
1390 xList->setFactor( bRecip ? (1.0 / fFactor) : fFactor );
1391 if( aDataVec.size() >= 3 )
1392 xList->setUnitName( aDataVec[ 2 ] );
1397 Config::Config( const char* pcEnvVar, const FilterBase& rFilter )
1399 construct( pcEnvVar, rFilter );
1402 Config::Config( const char* pcEnvVar, const Reference< XComponentContext >& rxContext, const StorageRef& rxRootStrg, const OUString& rSysFileName )
1404 construct( pcEnvVar, rxContext, rxRootStrg, rSysFileName );
1407 Config::~Config()
1411 void Config::construct( const char* pcEnvVar, const FilterBase& rFilter )
1413 if( !rFilter.getFileUrl().isEmpty() )
1414 construct( pcEnvVar, rFilter.getComponentContext(), rFilter.getStorage(), rFilter.getFileUrl() );
1417 void Config::construct( const char* pcEnvVar, const Reference< XComponentContext >& rxContext, const StorageRef& rxRootStrg, const OUString& rSysFileName )
1419 if( pcEnvVar && rxRootStrg && !rSysFileName.isEmpty() )
1420 if( const char* pcFileName = ::getenv( pcEnvVar ) )
1421 mxCfgData = std::make_shared<SharedConfigData>( OUString::createFromAscii( pcFileName ), rxContext, rxRootStrg, rSysFileName );
1424 const OUString& Config::getStringOption( const String& rKey, const OUString& rDefault ) const
1426 const OUString* pData = implGetOption( rKey );
1427 return pData ? *pData : rDefault;
1430 bool Config::getBoolOption( const String& rKey, bool bDefault ) const
1432 const OUString* pData = implGetOption( rKey );
1433 return pData ? StringHelper::convertStringToBool( *pData ) : bDefault;
1436 bool Config::isDumperEnabled() const
1438 return getBoolOption( "enable-dumper", false );
1441 bool Config::isImportEnabled() const
1443 return getBoolOption( "enable-import", true );
1446 void Config::eraseNameList( const String& rListName )
1448 mxCfgData->eraseNameList( rListName );
1451 NameListRef Config::getNameList( const String& rListName ) const
1453 return mxCfgData->getNameList( rListName );
1456 bool Config::implIsValid() const
1458 return isValid( mxCfgData );
1461 const OUString* Config::implGetOption( const OUString& rKey ) const
1463 return mxCfgData->getOption( rKey );
1466 Output::Output( const Reference< XComponentContext >& rxContext, const OUString& rFileName ) :
1467 mxStrm( InputOutputHelper::openTextOutputStream( rxContext, rFileName, RTL_TEXTENCODING_UTF8 ) ),
1468 mnCol( 0 ),
1469 mnItemLevel( 0 ),
1470 mnMultiLevel( 0 ),
1471 mnItemIdx( 0 ),
1472 mnLastItem( 0 )
1474 if( mxStrm.is() )
1475 mxStrm->writeString( OUString( OOX_DUMP_BOM ) );
1478 void Output::newLine()
1480 if( maLine.getLength() > 0 )
1482 mxStrm->writeString( maIndent );
1483 maLine.append( '\n' );
1484 mxStrm->writeString( maLine.makeStringAndClear() );
1485 mnCol = 0;
1486 mnLastItem = 0;
1490 void Output::emptyLine( size_t nCount )
1492 for( size_t nIdx = 0; nIdx < nCount; ++nIdx )
1493 mxStrm->writeString( OUString('\n') );
1496 void Output::incIndent()
1498 OUStringBuffer aBuffer( maIndent );
1499 StringHelper::appendChar( aBuffer, ' ', OOX_DUMP_INDENT );
1500 maIndent = aBuffer.makeStringAndClear();
1503 void Output::decIndent()
1505 if( maIndent.getLength() >= OOX_DUMP_INDENT )
1506 maIndent = maIndent.copy( OOX_DUMP_INDENT );
1509 void Output::startTable( sal_Int32 nW1 )
1511 startTable( 1, &nW1 );
1514 void Output::startTable( sal_Int32 nW1, sal_Int32 nW2 )
1516 sal_Int32 pnColWidths[ 2 ];
1517 pnColWidths[ 0 ] = nW1;
1518 pnColWidths[ 1 ] = nW2;
1519 startTable( 2, pnColWidths );
1522 void Output::startTable( sal_Int32 nW1, sal_Int32 nW2, sal_Int32 nW3, sal_Int32 nW4 )
1524 sal_Int32 pnColWidths[ 4 ];
1525 pnColWidths[ 0 ] = nW1;
1526 pnColWidths[ 1 ] = nW2;
1527 pnColWidths[ 2 ] = nW3;
1528 pnColWidths[ 3 ] = nW4;
1529 startTable( 4, pnColWidths );
1532 void Output::startTable( size_t nColCount, const sal_Int32* pnColWidths )
1534 maColPos.clear();
1535 maColPos.push_back( 0 );
1536 sal_Int32 nColPos = 0;
1537 for( size_t nCol = 0; nCol < nColCount; ++nCol )
1539 nColPos = nColPos + pnColWidths[ nCol ];
1540 maColPos.push_back( nColPos );
1544 void Output::tab()
1546 tab( mnCol + 1 );
1549 void Output::tab( size_t nCol )
1551 mnCol = nCol;
1552 if( mnCol < maColPos.size() )
1554 sal_Int32 nColPos = maColPos[ mnCol ];
1555 if( maLine.getLength() >= nColPos )
1556 maLine.setLength( ::std::max< sal_Int32 >( nColPos - 1, 0 ) );
1557 StringHelper::appendChar( maLine, ' ', nColPos - maLine.getLength() );
1559 else
1561 StringHelper::appendChar( maLine, ' ', 2 );
1565 void Output::endTable()
1567 maColPos.clear();
1570 void Output::resetItemIndex( sal_Int64 nIdx )
1572 mnItemIdx = nIdx;
1575 void Output::startItem( const String& rItemName )
1577 if( mnItemLevel == 0 )
1579 if( (mnMultiLevel > 0) && (maLine.getLength() > 0) )
1580 tab();
1581 if( rItemName.has() )
1583 writeItemName( rItemName );
1584 writeChar( OOX_DUMP_ITEMSEP );
1587 ++mnItemLevel;
1588 mnLastItem = maLine.getLength();
1591 void Output::contItem()
1593 if( mnItemLevel > 0 )
1595 if( (maLine.getLength() == 0) || (maLine[ maLine.getLength() - 1 ] != OOX_DUMP_ITEMSEP) )
1596 writeChar( OOX_DUMP_ITEMSEP );
1597 mnLastItem = maLine.getLength();
1601 void Output::endItem()
1603 if( mnItemLevel > 0 )
1605 maLastItem = maLine.copy( mnLastItem ).makeStringAndClear();
1606 if( maLastItem.isEmpty() && mnLastItem > 0 && maLine[ mnLastItem - 1 ] == OOX_DUMP_ITEMSEP )
1607 maLine.setLength( mnLastItem - 1 );
1608 --mnItemLevel;
1610 if( mnItemLevel == 0 )
1612 if( mnMultiLevel == 0 )
1613 newLine();
1615 else
1616 contItem();
1619 void Output::startMultiItems()
1621 ++mnMultiLevel;
1624 void Output::endMultiItems()
1626 if( mnMultiLevel > 0 )
1627 --mnMultiLevel;
1628 if( mnMultiLevel == 0 )
1629 newLine();
1632 void Output::writeChar( sal_Unicode cChar, sal_Int32 nCount )
1634 StringHelper::appendEncChar( maLine, cChar, nCount );
1637 void Output::writeAscii( const char* pcStr )
1639 if( pcStr )
1640 maLine.appendAscii( pcStr );
1643 void Output::writeString( std::u16string_view rStr )
1645 StringHelper::appendEncString( maLine, rStr );
1648 void Output::writeArray( const sal_uInt8* pnData, std::size_t nSize, sal_Unicode cSep )
1650 const sal_uInt8* pnEnd = pnData ? (pnData + nSize) : nullptr;
1651 for( const sal_uInt8* pnByte = pnData; pnByte < pnEnd; ++pnByte )
1653 if( pnByte > pnData )
1654 writeChar( cSep );
1655 writeHex( *pnByte, false );
1659 void Output::writeBool( bool bData )
1661 StringHelper::appendBool( maLine, bData );
1664 void Output::writeDateTime( const util::DateTime& rDateTime )
1666 writeDec( rDateTime.Year, 4, '0' );
1667 writeChar( '-' );
1668 writeDec( rDateTime.Month, 2, '0' );
1669 writeChar( '-' );
1670 writeDec( rDateTime.Day, 2, '0' );
1671 writeChar( 'T' );
1672 writeDec( rDateTime.Hours, 2, '0' );
1673 writeChar( ':' );
1674 writeDec( rDateTime.Minutes, 2, '0' );
1675 writeChar( ':' );
1676 writeDec( rDateTime.Seconds, 2, '0' );
1679 bool Output::implIsValid() const
1681 return mxStrm.is();
1684 void Output::writeItemName( const String& rItemName )
1686 if( rItemName.has() && (rItemName[ 0 ] == '#') )
1688 writeString( rItemName.subView( 1 ) );
1689 StringHelper::appendIndex( maLine, mnItemIdx++ );
1691 else
1692 writeString( rItemName );
1695 StorageIterator::StorageIterator( StorageRef xStrg ) :
1696 mxStrg(std::move( xStrg ))
1698 if( mxStrg )
1699 mxStrg->getElementNames( maNames );
1700 maIt = maNames.begin();
1703 StorageIterator::~StorageIterator()
1707 StorageIterator& StorageIterator::operator++()
1709 if( maIt != maNames.end() )
1710 ++maIt;
1711 return *this;
1714 OUString StorageIterator::getName() const
1716 OUString aName;
1717 if( maIt != maNames.end() )
1718 aName = *maIt;
1719 return aName;
1722 bool StorageIterator::isStream() const
1724 return isValid() && mxStrg->openInputStream( *maIt ).is();
1727 bool StorageIterator::isStorage() const
1729 if( !isValid() )
1730 return false;
1731 StorageRef xStrg = mxStrg->openSubStorage( *maIt, false );
1732 return xStrg && xStrg->isStorage();
1735 bool StorageIterator::implIsValid() const
1737 return mxStrg && mxStrg->isStorage() && (maIt != maNames.end());
1740 ObjectBase::~ObjectBase()
1744 void ObjectBase::construct( const ConfigRef& rxConfig )
1746 mxConfig = rxConfig;
1749 void ObjectBase::construct( const ObjectBase& rParent )
1751 *this = rParent;
1754 void ObjectBase::dump()
1756 if( isValid() )
1757 implDump();
1760 bool ObjectBase::implIsValid() const
1762 return isValid( mxConfig );
1765 void ObjectBase::implDump()
1769 void StorageObjectBase::construct( const ObjectBase& rParent, const StorageRef& rxStrg, const OUString& rSysPath )
1771 ObjectBase::construct( rParent );
1772 mxStrg = rxStrg;
1773 maSysPath = rSysPath;
1776 void StorageObjectBase::construct( const ObjectBase& rParent )
1778 ObjectBase::construct( rParent );
1779 if( ObjectBase::implIsValid() )
1781 mxStrg = cfg().getRootStorage();
1782 maSysPath = cfg().getSysFileName();
1786 bool StorageObjectBase::implIsValid() const
1788 return mxStrg && !maSysPath.isEmpty() && ObjectBase::implIsValid();
1791 void StorageObjectBase::implDump()
1793 bool bIsStrg = mxStrg->isStorage();
1794 bool bIsRoot = mxStrg->isRootStorage();
1795 Reference< XInputStream > xBaseStrm;
1796 if( !bIsStrg )
1797 xBaseStrm = mxStrg->openInputStream( OUString() );
1799 OUString aSysOutPath = maSysPath;
1800 if( bIsRoot ) try
1802 aSysOutPath += OOX_DUMP_DUMPEXT;
1803 Reference<XSimpleFileAccess3> xFileAccess(SimpleFileAccess::create(getContext()));
1804 xFileAccess->kill( aSysOutPath );
1806 catch( Exception& )
1810 if( bIsStrg )
1812 extractStorage( mxStrg, OUString(), aSysOutPath );
1814 else if( xBaseStrm.is() )
1816 BinaryInputStreamRef xInStrm( std::make_shared<BinaryXInputStream>( xBaseStrm, false ) );
1817 xInStrm->seekToStart();
1818 implDumpBaseStream( xInStrm, aSysOutPath );
1822 void StorageObjectBase::implDumpStream( const Reference< XInputStream >&, const OUString&, const OUString&, const OUString& )
1826 void StorageObjectBase::implDumpStorage( const StorageRef& rxStrg, const OUString& rStrgPath, const OUString& rSysPath )
1828 extractStorage( rxStrg, rStrgPath, rSysPath );
1831 void StorageObjectBase::implDumpBaseStream( const BinaryInputStreamRef&, const OUString& )
1835 void StorageObjectBase::addPreferredStream( const String& rStrmName )
1837 if( rStrmName.has() )
1838 maPreferred.emplace_back( rStrmName, false );
1841 void StorageObjectBase::addPreferredStorage( const String& rStrgPath )
1843 if( rStrgPath.has() )
1844 maPreferred.emplace_back( rStrgPath, true );
1847 OUString StorageObjectBase::getSysFileName(
1848 std::u16string_view rStrmName, std::u16string_view rSysOutPath )
1850 // encode all characters < 0x20
1851 OUStringBuffer aBuffer;
1852 StringHelper::appendEncString( aBuffer, rStrmName, false );
1854 // replace all characters reserved in file system
1855 OUString aFileName = aBuffer.makeStringAndClear();
1856 static const sal_Unicode spcReserved[] = { '/', '\\', ':', '*', '?', '<', '>', '|' };
1857 for(const sal_Unicode cChar : spcReserved)
1858 aFileName = aFileName.replace(cChar, '_');
1860 // build full path
1861 return OUString::Concat(rSysOutPath) + "/" + aFileName;
1864 void StorageObjectBase::extractStream( StorageBase& rStrg, const OUString& rStrgPath, const OUString& rStrmName, const OUString& rSysFileName )
1866 BinaryXInputStream aInStrm( rStrg.openInputStream( rStrmName ), true );
1867 if( !aInStrm.isEof() )
1869 BinaryXOutputStream aOutStrm( InputOutputHelper::openOutputStream( getContext(), rSysFileName ), true );
1870 if( !aOutStrm.isEof() )
1871 aInStrm.copyToStream( aOutStrm );
1873 Reference< XInputStream > xDumpStrm = InputOutputHelper::openInputStream( getContext(), rSysFileName );
1874 if( xDumpStrm.is() )
1875 implDumpStream( xDumpStrm, rStrgPath, rStrmName, rSysFileName );
1878 void StorageObjectBase::extractStorage( const StorageRef& rxStrg, const OUString& rStrgPath, const OUString& rSysPath )
1880 // create directory in file system
1881 ::osl::FileBase::RC eRes = ::osl::Directory::create( rSysPath );
1882 if( (eRes != ::osl::FileBase::E_None) && (eRes != ::osl::FileBase::E_EXIST) )
1883 return;
1885 // process preferred storages and streams in root storage first
1886 if( rStrgPath.isEmpty() )
1888 for (auto const& elemPreferred : maPreferred)
1889 extractItem( rxStrg, rStrgPath, elemPreferred.maName, rSysPath, elemPreferred.mbStorage, !elemPreferred.mbStorage );
1892 // process children of the storage
1893 for( StorageIterator aIt( rxStrg ); aIt.isValid(); ++aIt )
1895 // skip processed preferred items
1896 OUString aItemName = aIt.getName();
1897 bool bFound = false;
1898 if( rStrgPath.isEmpty() )
1900 for (auto const& elemPreferred : maPreferred)
1902 bFound = elemPreferred.maName == aItemName;
1903 if (bFound)
1904 break;
1907 if( !bFound )
1908 extractItem( rxStrg, rStrgPath, aItemName, rSysPath, aIt.isStorage(), aIt.isStream() );
1912 void StorageObjectBase::extractItem( const StorageRef& rxStrg, const OUString& rStrgPath, const OUString& rItemName, std::u16string_view rSysPath, bool bIsStrg, bool bIsStrm )
1914 OUString aSysFileName = getSysFileName( rItemName, rSysPath );
1915 if( bIsStrg )
1917 OUStringBuffer aStrgPath( rStrgPath );
1918 StringHelper::appendToken( aStrgPath, rItemName, '/' );
1919 implDumpStorage( rxStrg->openSubStorage( rItemName, false ), aStrgPath.makeStringAndClear(), aSysFileName );
1921 else if( bIsStrm )
1923 extractStream( *rxStrg, rStrgPath, rItemName, aSysFileName );
1927 OutputObjectBase::~OutputObjectBase()
1931 void OutputObjectBase::construct( const ObjectBase& rParent, const OUString& rSysFileName )
1933 ObjectBase::construct( rParent );
1934 if( ObjectBase::implIsValid() )
1936 maSysFileName = rSysFileName;
1937 mxOut = std::make_shared<Output>( getContext(), rSysFileName + OOX_DUMP_DUMPEXT );
1941 void OutputObjectBase::construct( const OutputObjectBase& rParent )
1943 *this = rParent;
1946 bool OutputObjectBase::implIsValid() const
1948 return isValid( mxOut ) && ObjectBase::implIsValid();
1951 void OutputObjectBase::writeEmptyItem( const String& rName )
1953 ItemGuard aItem( mxOut, rName );
1956 void OutputObjectBase::writeInfoItem( const String& rName, const String& rData )
1958 ItemGuard aItem( mxOut, rName );
1959 mxOut->writeString( rData );
1962 void OutputObjectBase::writeCharItem( const String& rName, sal_Unicode cData )
1964 ItemGuard aItem( mxOut, rName );
1965 mxOut->writeChar( OOX_DUMP_STRQUOTE );
1966 mxOut->writeChar( cData );
1967 mxOut->writeChar( OOX_DUMP_STRQUOTE );
1970 void OutputObjectBase::writeStringItem( const String& rName, std::u16string_view rData )
1972 ItemGuard aItem( mxOut, rName );
1973 mxOut->writeAscii( "(len=" );
1974 mxOut->writeDec( sal_Int32(rData.size()) );
1975 mxOut->writeAscii( ")," );
1976 OUStringBuffer aValue( rData.substr( 0, ::std::min( sal_Int32(rData.size()), OOX_DUMP_MAXSTRLEN ) ) );
1977 StringHelper::enclose( aValue, OOX_DUMP_STRQUOTE );
1978 mxOut->writeString( aValue.makeStringAndClear() );
1979 if( rData.size() > OOX_DUMP_MAXSTRLEN )
1980 mxOut->writeAscii( ",cut" );
1983 void OutputObjectBase::writeArrayItem( const String& rName, const sal_uInt8* pnData, std::size_t nSize, sal_Unicode cSep )
1985 ItemGuard aItem( mxOut, rName );
1986 mxOut->writeArray( pnData, nSize, cSep );
1989 void OutputObjectBase::writeDateTimeItem( const String& rName, const util::DateTime& rDateTime )
1991 ItemGuard aItem( mxOut, rName );
1992 mxOut->writeDateTime( rDateTime );
1995 void OutputObjectBase::writeGuidItem( const String& rName, const OUString& rGuid )
1997 ItemGuard aItem( mxOut, rName );
1998 mxOut->writeString( rGuid );
1999 aItem.cont();
2000 mxOut->writeString( cfg().getStringOption( rGuid, OUString() ) );
2003 InputObjectBase::~InputObjectBase()
2007 void InputObjectBase::construct( const ObjectBase& rParent, const BinaryInputStreamRef& rxStrm, const OUString& rSysFileName )
2009 OutputObjectBase::construct( rParent, rSysFileName );
2010 mxStrm = rxStrm;
2013 void InputObjectBase::construct( const OutputObjectBase& rParent, const BinaryInputStreamRef& rxStrm )
2015 OutputObjectBase::construct( rParent );
2016 mxStrm = rxStrm;
2019 void InputObjectBase::construct( const InputObjectBase& rParent )
2021 *this = rParent;
2024 bool InputObjectBase::implIsValid() const
2026 return mxStrm && OutputObjectBase::implIsValid();
2029 void InputObjectBase::skipBlock( sal_Int64 nBytes, bool bShowSize )
2031 sal_Int64 nEndPos = ::std::min< sal_Int64 >( mxStrm->tell() + nBytes, mxStrm->size() );
2032 if( mxStrm->tell() < nEndPos )
2034 if( bShowSize )
2035 writeDecItem( "skipped-data-size", static_cast< sal_uInt64 >( nEndPos - mxStrm->tell() ) );
2036 mxStrm->seek( nEndPos );
2040 void InputObjectBase::dumpRawBinary( sal_Int64 nBytes, bool bShowOffset, bool bStream )
2042 TableGuard aTabGuard( mxOut,
2043 bShowOffset ? 12 : 0,
2044 3 * OOX_DUMP_BYTESPERLINE / 2 + 1,
2045 3 * OOX_DUMP_BYTESPERLINE / 2 + 1,
2046 OOX_DUMP_BYTESPERLINE / 2 + 1 );
2048 sal_Int64 nMaxShowSize = cfg().getIntOption< sal_Int64 >(
2049 bStream ? "max-binary-stream-size" : "max-binary-data-size", SAL_MAX_INT64 );
2051 bool bSeekable = mxStrm->size() >= 0;
2052 sal_Int64 nEndPos = bSeekable ? ::std::min< sal_Int64 >( mxStrm->tell() + nBytes, mxStrm->size() ) : 0;
2053 sal_Int64 nDumpEnd = bSeekable ? ::std::min< sal_Int64 >( mxStrm->tell() + nMaxShowSize, nEndPos ) : nMaxShowSize;
2054 sal_Int64 nPos = bSeekable ? mxStrm->tell() : 0;
2055 bool bLoop = true;
2057 while( bLoop && (nPos < nDumpEnd) )
2059 mxOut->writeHex( static_cast< sal_uInt32 >( nPos ) );
2060 mxOut->tab();
2062 sal_uInt8 pnLineData[ OOX_DUMP_BYTESPERLINE ];
2063 sal_Int32 nLineSize = bSeekable ? ::std::min( static_cast< sal_Int32 >( nDumpEnd - mxStrm->tell() ), OOX_DUMP_BYTESPERLINE ) : OOX_DUMP_BYTESPERLINE;
2064 sal_Int32 nReadSize = mxStrm->readMemory( pnLineData, nLineSize );
2065 bLoop = nReadSize == nLineSize;
2066 nPos += nReadSize;
2068 if( nReadSize > 0 )
2070 const sal_uInt8* pnByte = nullptr;
2071 const sal_uInt8* pnEnd = nullptr;
2072 for( pnByte = pnLineData, pnEnd = pnLineData + nReadSize; pnByte != pnEnd; ++pnByte )
2074 if( (pnByte - pnLineData) == (OOX_DUMP_BYTESPERLINE / 2) ) mxOut->tab();
2075 mxOut->writeHex( *pnByte, false );
2076 mxOut->writeChar( ' ' );
2079 aTabGuard.tab( 3 );
2080 for( pnByte = pnLineData, pnEnd = pnLineData + nReadSize; pnByte != pnEnd; ++pnByte )
2082 if( (pnByte - pnLineData) == (OOX_DUMP_BYTESPERLINE / 2) ) mxOut->tab();
2083 mxOut->writeChar( static_cast< sal_Unicode >( (*pnByte < 0x20) ? '.' : *pnByte ) );
2085 mxOut->newLine();
2089 // skip undumped data
2090 if( bSeekable )
2091 skipBlock( nEndPos - mxStrm->tell() );
2094 void InputObjectBase::dumpBinary( const String& rName, sal_Int64 nBytes, bool bShowOffset )
2097 MultiItemsGuard aMultiGuard( mxOut );
2098 writeEmptyItem( rName );
2099 writeDecItem( "size", nBytes );
2101 IndentGuard aIndGuard( mxOut );
2102 dumpRawBinary( nBytes, bShowOffset );
2105 void InputObjectBase::dumpRemaining( sal_Int64 nBytes )
2107 if( nBytes > 0 )
2109 if( cfg().getBoolOption( "show-trailing-unknown", true ) )
2110 dumpBinary( "remaining-data", nBytes, false );
2111 else
2112 skipBlock( nBytes );
2116 void InputObjectBase::dumpRemainingTo( sal_Int64 nPos )
2118 if( mxStrm->isEof() || (mxStrm->tell() > nPos) )
2119 writeInfoItem( "stream-state", OOX_DUMP_ERR_STREAM );
2120 else
2121 dumpRemaining( nPos - mxStrm->tell() );
2122 mxStrm->seek( nPos );
2125 void InputObjectBase::dumpRemainingStream()
2127 dumpRemainingTo( mxStrm->size() );
2130 void InputObjectBase::dumpArray( const String& rName, sal_Int32 nBytes, sal_Unicode cSep )
2132 sal_Int32 nDumpSize = getLimitedValue< sal_Int32, sal_Int64 >( mxStrm->size() - mxStrm->tell(), 0, nBytes );
2133 if( nDumpSize > OOX_DUMP_MAXARRAY )
2135 dumpBinary( rName, nBytes, false );
2137 else if( nDumpSize > 1 )
2139 sal_uInt8 pnData[ OOX_DUMP_MAXARRAY ];
2140 mxStrm->readMemory( pnData, nDumpSize );
2141 writeArrayItem( rName, pnData, nDumpSize, cSep );
2143 else if( nDumpSize == 1 )
2144 dumpHex< sal_uInt8 >( rName );
2147 sal_Unicode InputObjectBase::dumpUnicode( const String& rName )
2149 sal_uInt16 nChar = mxStrm->readuInt16();
2150 sal_Unicode cChar = static_cast< sal_Unicode >( nChar );
2151 writeCharItem( rName( "char" ), cChar );
2152 return cChar;
2155 OUString InputObjectBase::dumpCharArray( const String& rName, sal_Int32 nLen, rtl_TextEncoding eTextEnc, bool bHideTrailingNul )
2157 sal_Int32 nDumpSize = getLimitedValue< sal_Int32, sal_Int64 >( mxStrm->size() - mxStrm->tell(), 0, nLen );
2158 OUString aString;
2159 if( nDumpSize > 0 )
2161 ::std::vector< char > aBuffer( static_cast< std::size_t >( nLen ) + 1 );
2162 sal_Int32 nCharsRead = mxStrm->readMemory(aBuffer.data(), nLen);
2163 aBuffer[ nCharsRead ] = 0;
2164 aString = OStringToOUString(std::string_view(aBuffer.data()), eTextEnc);
2166 if( bHideTrailingNul )
2167 aString = StringHelper::trimTrailingNul( aString );
2168 writeStringItem( rName( "text" ), aString );
2169 return aString;
2172 OUString InputObjectBase::dumpUnicodeArray( const String& rName, sal_Int32 nLen, bool bHideTrailingNul )
2174 OUStringBuffer aBuffer;
2175 for( sal_Int32 nIndex = 0; !mxStrm->isEof() && (nIndex < nLen); ++nIndex )
2177 aBuffer.append( static_cast< sal_Unicode >( mxStrm->readuInt16() ) );
2179 OUString aString = aBuffer.makeStringAndClear();
2180 if( bHideTrailingNul )
2181 aString = StringHelper::trimTrailingNul( aString );
2182 writeStringItem( rName( "text" ), aString );
2183 return aString;
2186 util::DateTime InputObjectBase::dumpFileTime( const String& rName )
2188 util::DateTime aDateTime;
2190 ItemGuard aItem( mxOut, rName( "file-time" ) );
2191 sal_Int64 nFileTime = dumpDec< sal_Int64 >( EMPTY_STRING );
2192 // file time is in 10^-7 seconds (100 nanoseconds), convert to nanoseconds
2193 nFileTime *= 100;
2194 // entire days
2195 sal_Int64 nDays = nFileTime / sal_Int64( ::tools::Time::nanoSecPerDay );
2196 // number of entire years
2197 sal_Int64 nYears = (nDays - (nDays / (4 * 365)) + (nDays / (100 * 365)) - (nDays / (400 * 365))) / 365;
2198 // remaining days in the year
2199 sal_Int64 nDaysInYear = nDays - (nYears * 365 + nYears / 4 - nYears / 100 + nYears / 400);
2200 // the year (file dates start from 1601-01-01)
2201 aDateTime.Year = static_cast< sal_uInt16 >( 1601 + nYears );
2202 // leap year?
2203 bool bLeap = ((aDateTime.Year % 4 == 0) && (aDateTime.Year % 100 != 0)) || (aDateTime.Year % 400 == 0);
2204 // static arrays with number of days in month
2205 static const sal_Int64 spnDaysInMonth[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
2206 static const sal_Int64 spnDaysInMonthL[] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
2207 const sal_Int64* pnDaysInMonth = bLeap ? spnDaysInMonthL : spnDaysInMonth;
2208 // the month
2209 aDateTime.Month = 1;
2210 while( nDaysInYear >= *pnDaysInMonth )
2212 nDaysInYear -= *pnDaysInMonth++;
2213 ++aDateTime.Month;
2215 // the day
2216 aDateTime.Day = static_cast< sal_uInt16 >( nDaysInYear + 1 );
2217 // number of nanoseconds in the day
2218 sal_Int64 nTimeInDay = nFileTime % sal_Int64( ::tools::Time::nanoSecPerDay );
2219 // nanoseconds
2220 aDateTime.NanoSeconds = static_cast< sal_uInt32 >( nTimeInDay % ::tools::Time::nanoSecPerSec );
2221 nTimeInDay /= ::tools::Time::nanoSecPerSec;
2222 // seconds
2223 aDateTime.Seconds = static_cast< sal_uInt16 >( nTimeInDay % ::tools::Time::secondPerMinute );
2224 nTimeInDay /= ::tools::Time::secondPerMinute;
2225 // minutes
2226 aDateTime.Minutes = static_cast< sal_uInt16 >( nTimeInDay % ::tools::Time::minutePerHour );
2227 nTimeInDay /= ::tools::Time::minutePerHour;
2228 // hours
2229 aDateTime.Hours = static_cast< sal_uInt16 >( nTimeInDay );
2231 writeDateTimeItem( EMPTY_STRING, aDateTime );
2232 return aDateTime;
2235 OUString InputObjectBase::dumpGuid( const String& rName )
2237 OUStringBuffer aBuffer;
2238 sal_uInt32 nData32;
2239 sal_uInt16 nData16;
2240 sal_uInt8 nData8;
2242 nData32 = mxStrm->readuInt32();
2243 StringHelper::appendHex( aBuffer, nData32, false );
2244 aBuffer.append( '-' );
2245 nData16 = mxStrm->readuInt16();
2246 StringHelper::appendHex( aBuffer, nData16, false );
2247 aBuffer.append( '-' );
2248 nData16 = mxStrm->readuInt16();
2249 StringHelper::appendHex( aBuffer, nData16, false );
2250 aBuffer.append( '-' );
2251 nData8 = mxStrm->readuChar();
2252 StringHelper::appendHex( aBuffer, nData8, false );
2253 nData8 = mxStrm->readuChar( );
2254 StringHelper::appendHex( aBuffer, nData8, false );
2255 aBuffer.append( '-' );
2256 for( int nIndex = 0; nIndex < 6; ++nIndex )
2258 nData8 = mxStrm->readuChar( );
2259 StringHelper::appendHex( aBuffer, nData8, false );
2261 StringHelper::enclose( aBuffer, '{', '}' );
2262 OUString aGuid = aBuffer.makeStringAndClear();
2263 writeGuidItem( rName( "guid" ), aGuid );
2264 return aGuid;
2267 void InputObjectBase::dumpItem( const ItemFormat& rItemFmt )
2269 switch( rItemFmt.meDataType )
2271 case DATATYPE_VOID: break;
2272 case DATATYPE_INT8: dumpValue< sal_Int8 >( rItemFmt ); break;
2273 case DATATYPE_UINT8: dumpValue< sal_uInt8 >( rItemFmt ); break;
2274 case DATATYPE_INT16: dumpValue< sal_Int16 >( rItemFmt ); break;
2275 case DATATYPE_UINT16: dumpValue< sal_uInt16 >( rItemFmt ); break;
2276 case DATATYPE_INT32: dumpValue< sal_Int32 >( rItemFmt ); break;
2277 case DATATYPE_UINT32: dumpValue< sal_uInt32 >( rItemFmt ); break;
2278 case DATATYPE_INT64: dumpValue< sal_Int64 >( rItemFmt ); break;
2279 case DATATYPE_UINT64: dumpValue< sal_uInt64 >( rItemFmt ); break;
2280 case DATATYPE_FLOAT: dumpValue< float >( rItemFmt ); break;
2281 case DATATYPE_DOUBLE: dumpValue< double >( rItemFmt ); break;
2282 default:;
2286 BinaryStreamObject::BinaryStreamObject( const ObjectBase& rParent, const BinaryInputStreamRef& rxStrm, const OUString& rSysFileName )
2288 InputObjectBase::construct( rParent, rxStrm, rSysFileName );
2291 void BinaryStreamObject::dumpBinaryStream( bool bShowOffset )
2293 mxStrm->seekToStart();
2294 dumpRawBinary( mxStrm->size(), bShowOffset, true );
2295 mxOut->emptyLine();
2298 void BinaryStreamObject::implDump()
2300 dumpBinaryStream();
2303 void TextStreamObjectBase::construct( const ObjectBase& rParent,
2304 const BinaryInputStreamRef& rxStrm, rtl_TextEncoding eTextEnc, const OUString& rSysFileName )
2306 InputObjectBase::construct( rParent, rxStrm, rSysFileName );
2307 constructTextStrmObj( eTextEnc );
2310 void TextStreamObjectBase::construct( const OutputObjectBase& rParent,
2311 const BinaryInputStreamRef& rxStrm, rtl_TextEncoding eTextEnc )
2313 InputObjectBase::construct( rParent, rxStrm );
2314 constructTextStrmObj( eTextEnc );
2317 bool TextStreamObjectBase::implIsValid() const
2319 return InputObjectBase::implIsValid() && mxTextStrm;
2322 void TextStreamObjectBase::implDump()
2324 implDumpText( *mxTextStrm );
2327 void TextStreamObjectBase::constructTextStrmObj( rtl_TextEncoding eTextEnc )
2329 if( mxStrm )
2330 mxTextStrm = std::make_shared<TextInputStream>( getContext(), *mxStrm, eTextEnc );
2333 TextLineStreamObject::TextLineStreamObject( const ObjectBase& rParent,
2334 const BinaryInputStreamRef& rxStrm, rtl_TextEncoding eTextEnc, const OUString& rSysFileName )
2336 TextStreamObjectBase::construct( rParent, rxStrm, eTextEnc, rSysFileName );
2339 TextLineStreamObject::TextLineStreamObject( const OutputObjectBase& rParent,
2340 const BinaryInputStreamRef& rxStrm, rtl_TextEncoding eTextEnc )
2342 TextStreamObjectBase::construct( rParent, rxStrm, eTextEnc );
2345 void TextLineStreamObject::implDumpText( TextInputStream& rTextStrm )
2347 sal_uInt32 nLine = 0;
2348 while( !rTextStrm.isEof() )
2350 OUString aLine = rTextStrm.readLine();
2351 if( !rTextStrm.isEof() || !aLine.isEmpty() )
2352 implDumpLine( aLine, ++nLine );
2356 void TextLineStreamObject::implDumpLine( std::u16string_view rLine, sal_uInt32 nLine )
2358 TableGuard aTabGuard( mxOut, 8 );
2359 mxOut->writeDec( nLine, 6 );
2360 mxOut->tab();
2361 mxOut->writeString( rLine );
2362 mxOut->newLine();
2365 XmlStreamObject::XmlStreamObject( const ObjectBase& rParent,
2366 const BinaryInputStreamRef& rxStrm, const OUString& rSysFileName )
2368 TextStreamObjectBase::construct( rParent, rxStrm, RTL_TEXTENCODING_UTF8, rSysFileName );
2371 void XmlStreamObject::implDumpText( TextInputStream& rTextStrm )
2373 /* Buffers a start element and the following element text. Needed to dump
2374 matching start/end elements and the element text on the same line. */
2375 OUStringBuffer aOldStartElem;
2376 // special handling for VML
2377 bool bIsVml = o3tl::equalsIgnoreAsciiCase(InputOutputHelper::getFileNameExtension( maSysFileName ), u"vml");
2379 while( !rTextStrm.isEof() )
2381 // get the next element and the following element text from text stream
2382 OUString aElem = rTextStrm.readToChar( '>', true ).trim();
2383 OUString aText = rTextStrm.readToChar( '<', false );
2385 // remove multiple whitespace from element
2386 sal_Int32 nPos = 0;
2387 while( nPos < aElem.getLength() )
2389 while( (nPos < aElem.getLength()) && (aElem[ nPos ] >= 32) ) ++nPos;
2390 if( nPos < aElem.getLength() )
2391 aElem = aElem.subView( 0, nPos ) + OUStringChar(' ') + o3tl::trim(aElem.subView( nPos ));
2392 ++nPos;
2395 sal_Int32 nElemLen = aElem.getLength();
2396 if( (nElemLen >= 2) && (aElem[ 0 ] == '<') && (aElem[ nElemLen - 1 ] == '>') )
2398 // determine type of the element
2399 bool bSimpleElem = (aElem[ 1 ] == '!') || (aElem[ 1 ] == '?') || (aElem[ nElemLen - 2 ] == '/') ||
2400 (bIsVml && (nElemLen == 4) && (aElem[ 1 ] == 'b') && (aElem[ 2 ] == 'r'));
2401 bool bStartElem = !bSimpleElem && (aElem[ 1 ] != '/');
2402 bool bEndElem = !bSimpleElem && !bStartElem;
2404 /* Start element or simple element: flush old start element and
2405 its text from previous iteration, and start a new indentation
2406 level for the new element. Trim whitespace and line breaks from
2407 the text of the old start element. */
2408 if( (bSimpleElem || bStartElem) && (aOldStartElem.getLength() > 0) )
2410 mxOut->writeString( o3tl::trim(aOldStartElem.makeStringAndClear()) );
2411 mxOut->newLine();
2412 mxOut->incIndent();
2415 /* Start element: remember it and its text, to be able to print the
2416 matching end element on the same line in the next iteration. */
2417 if( bStartElem )
2419 aOldStartElem.append( aElem + aText );
2421 else
2423 /* End element: if a start element has been remembered in the
2424 previous iteration, write it out here untrimmed, to show
2425 all whitespace in the element text, and without trailing
2426 line break. Code below will add the end element right after
2427 it. Otherwise, return to previous indentation level. */
2428 if( bEndElem )
2430 if( aOldStartElem.getLength() == 0 )
2431 mxOut->decIndent();
2432 else
2433 mxOut->writeString( aOldStartElem.makeStringAndClear() );
2436 /* Write the element. Write following element text in a new
2437 line, but only, if it does not contain of white space
2438 entirely. */
2439 mxOut->writeString( aElem );
2440 mxOut->newLine();
2441 if( !o3tl::trim(aText).empty() )
2443 mxOut->writeString( aText );
2444 mxOut->newLine();
2451 void RecordObjectBase::construct( const ObjectBase& rParent,
2452 const BinaryInputStreamRef& rxBaseStrm, const OUString& rSysFileName,
2453 const BinaryInputStreamRef& rxRecStrm, const String& rRecNames, const String& rSimpleRecs )
2455 InputObjectBase::construct( rParent, rxRecStrm, rSysFileName );
2456 constructRecObjBase( rxBaseStrm, rRecNames, rSimpleRecs );
2459 bool RecordObjectBase::implIsValid() const
2461 return mxBaseStrm && InputObjectBase::implIsValid();
2464 void RecordObjectBase::implDump()
2466 NameListRef xRecNames = maRecNames.getNameList( cfg() );
2467 ItemFormatMap aSimpleRecs( maSimpleRecs.getNameList( cfg() ) );
2469 while( implStartRecord( *mxBaseStrm, mnRecPos, mnRecId, mnRecSize ) )
2471 // record header
2472 mxOut->emptyLine();
2473 writeHeader();
2474 implWriteExtHeader();
2475 IndentGuard aIndGuard( mxOut );
2476 sal_Int64 nRecPos = mxStrm->tell();
2478 // record body
2479 if( !mbBinaryOnly && cfg().hasName( xRecNames, mnRecId ) )
2481 ::std::map< sal_Int64, ItemFormat >::const_iterator aIt = aSimpleRecs.find( mnRecId );
2482 if( aIt != aSimpleRecs.end() )
2483 dumpItem( aIt->second );
2484 else
2485 implDumpRecordBody();
2488 // remaining undumped data
2489 if( !mxStrm->isEof() && (mxStrm->tell() == nRecPos) )
2490 dumpRawBinary( mnRecSize, false );
2491 else
2492 dumpRemainingTo( nRecPos + mnRecSize );
2496 void RecordObjectBase::implWriteExtHeader()
2500 void RecordObjectBase::implDumpRecordBody()
2504 void RecordObjectBase::constructRecObjBase( const BinaryInputStreamRef& rxBaseStrm, const String& rRecNames, const String& rSimpleRecs )
2506 mxBaseStrm = rxBaseStrm;
2507 maRecNames = rRecNames;
2508 maSimpleRecs = rSimpleRecs;
2509 mnRecPos = mnRecId = mnRecSize = 0;
2510 mbBinaryOnly = false;
2511 if( InputObjectBase::implIsValid() )
2512 mbShowRecPos = cfg().getBoolOption( "show-record-position", true );
2515 void RecordObjectBase::writeHeader()
2517 MultiItemsGuard aMultiGuard( mxOut );
2518 writeEmptyItem( "REC" );
2519 if( mbShowRecPos && mxBaseStrm->isSeekable() )
2520 writeShortHexItem( "pos", mnRecPos, "CONV-DEC" );
2521 writeShortHexItem( "size", mnRecSize, "CONV-DEC" );
2522 ItemGuard aItem( mxOut, "id" );
2523 mxOut->writeShortHex( mnRecId );
2524 addNameToItem( mnRecId, "CONV-DEC" );
2525 addNameToItem( mnRecId, maRecNames );
2528 void SequenceRecordObjectBase::construct( const ObjectBase& rParent,
2529 const BinaryInputStreamRef& rxBaseStrm, const OUString& rSysFileName,
2530 const String& rRecNames, const String& rSimpleRecs )
2532 BinaryInputStreamRef xRecStrm( std::make_shared<SequenceInputStream>( *mxRecData ) );
2533 RecordObjectBase::construct( rParent, rxBaseStrm, rSysFileName, xRecStrm, rRecNames, rSimpleRecs );
2536 bool SequenceRecordObjectBase::implStartRecord( BinaryInputStream& rBaseStrm, sal_Int64& ornRecPos, sal_Int64& ornRecId, sal_Int64& ornRecSize )
2538 bool bValid = true;
2539 if( rBaseStrm.isSeekable() )
2541 ornRecPos = rBaseStrm.tell();
2542 // do not try to overread seekable streams, may cause assertions
2543 bValid = ornRecPos < rBaseStrm.size();
2546 // read the record header
2547 if( bValid )
2548 bValid = implReadRecordHeader( rBaseStrm, ornRecId, ornRecSize ) && !rBaseStrm.isEof() && (0 <= ornRecSize) && (ornRecSize <= 0x00100000);
2550 // read record contents into data sequence
2551 if( bValid )
2553 sal_Int32 nRecSize = static_cast< sal_Int32 >( ornRecSize );
2554 mxRecData->realloc( nRecSize );
2555 bValid = (nRecSize == 0) || (rBaseStrm.readData( *mxRecData, nRecSize ) == nRecSize);
2556 mxStrm->seekToStart();
2558 return bValid;
2561 DumperBase::~DumperBase()
2565 bool DumperBase::isImportEnabled() const
2567 return !isValid() || cfg().isImportEnabled();
2570 void DumperBase::construct( const ConfigRef& rxConfig )
2572 if( isValid( rxConfig ) && rxConfig->isDumperEnabled() )
2573 ObjectBase::construct( rxConfig );
2576 } // namespace oox
2578 #endif
2580 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */