/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */
 
#include <memory>
#include <xihelper.hxx>
#include <svl/itemset.hxx>
#include <svl/sharedstringpool.hxx>
#include <editeng/editobj.hxx>
#include <tools/urlobj.hxx>
#include <editeng/eeitem.hxx>
#include <editeng/flditem.hxx>
#include <document.hxx>
#include <rangelst.hxx>
#include <editutil.hxx>
#include <attrib.hxx>
#include <xltracer.hxx>
#include <xistream.hxx>
#include <xistring.hxx>
#include <xistyle.hxx>
#include <excform.hxx>
#include <scmatrix.hxx>
#include <documentimport.hxx>
#include <sal/log.hxx>
 
// Excel->Calc cell address/range conversion ==================================
 
namespace {
 
/** Fills the passed Calc address with the passed Excel cell coordinates without checking any limits. */
void lclFillAddress( ScAddress& rScPos, sal_uInt16 nXclCol, sal_uInt32 nXclRow, SCTAB nScTab )
{
    rScPos.SetCol( static_cast< SCCOL >( nXclCol ) );
    rScPos.SetRow( static_cast< SCROW >( nXclRow ) );
    rScPos.SetTab( nScTab );
}
 
} // namespace
 
XclImpAddressConverter::XclImpAddressConverter( const XclImpRoot& rRoot ) :
    XclAddressConverterBase( rRoot.GetTracer(), rRoot.GetScMaxPos() )
{
}
 
// cell address ---------------------------------------------------------------
 
bool XclImpAddressConverter::CheckAddress( const XclAddress& rXclPos, bool bWarn )
{
    bool bValidCol = rXclPos.mnCol <= mnMaxCol;
    bool bValidRow = rXclPos.mnRow <= mnMaxRow;
    bool bValid = bValidCol && bValidRow;
    if( !bValid && bWarn )
    {
        mbColTrunc |= !bValidCol;
        mbRowTrunc |= !bValidRow;
        mrTracer.TraceInvalidAddress( ScAddress(
            static_cast< SCCOL >( rXclPos.mnCol ), static_cast< SCROW >( rXclPos.mnRow ), 0 ), maMaxPos );
    }
    return bValid;
}
 
bool XclImpAddressConverter::ConvertAddress( ScAddress& rScPos,
        const XclAddress& rXclPos, SCTAB nScTab, bool bWarn )
{
    bool bValid = CheckAddress( rXclPos, bWarn );
    if( bValid )
        lclFillAddress( rScPos, rXclPos.mnCol, rXclPos.mnRow, nScTab );
    return bValid;
}
 
ScAddress XclImpAddressConverter::CreateValidAddress(
        const XclAddress& rXclPos, SCTAB nScTab, bool bWarn )
{
    ScAddress aScPos( ScAddress::UNINITIALIZED );
    if( !ConvertAddress( aScPos, rXclPos, nScTab, bWarn ) )
    {
        aScPos.SetCol( static_cast< SCCOL >( ::std::min( rXclPos.mnCol, mnMaxCol ) ) );
        aScPos.SetRow( static_cast< SCROW >( ::std::min( rXclPos.mnRow, mnMaxRow ) ) );
        aScPos.SetTab( limit_cast< SCTAB >( nScTab, 0, maMaxPos.Tab() ) );
    }
    return aScPos;
}
 
// cell range -----------------------------------------------------------------
 
void XclImpAddressConverter::FillRange(const XclRange& rXclRange, ScRange& rScRange)
{
    const XclAddress aXclStartAdr = rXclRange.maFirst;
    lclFillAddress(rScRange.aStart, aXclStartAdr.mnCol, aXclStartAdr.mnRow, rScRange.aStart.Tab());
    const XclAddress aXclEndAdr = rXclRange.maLast;
    lclFillAddress(rScRange.aEnd, aXclEndAdr.mnCol, aXclEndAdr.mnRow, rScRange.aEnd.Tab());
}
 
bool XclImpAddressConverter::ConvertRange( ScRange& rScRange,
        const XclRange& rXclRange, SCTAB nScTab1, SCTAB nScTab2, bool bWarn )
{
    // check start position
    bool bValidStart = CheckAddress( rXclRange.maFirst, bWarn );
    if( bValidStart )
    {
        lclFillAddress( rScRange.aStart, rXclRange.maFirst.mnCol, rXclRange.maFirst.mnRow, nScTab1 );
 
        // check & correct end position
        sal_uInt16 nXclCol2 = rXclRange.maLast.mnCol;
        sal_uInt32 nXclRow2 = rXclRange.maLast.mnRow;
        if( !CheckAddress( rXclRange.maLast, bWarn ) )
        {
            nXclCol2 = ::std::min( nXclCol2, mnMaxCol );
            nXclRow2 = ::std::min( nXclRow2, mnMaxRow );
        }
        lclFillAddress( rScRange.aEnd, nXclCol2, nXclRow2, nScTab2 );
    }
    return bValidStart;
}
 
// cell range list ------------------------------------------------------------
 
void XclImpAddressConverter::ConvertRangeList( ScRangeList& rScRanges,
        const XclRangeList& rXclRanges, SCTAB nScTab, bool bWarn )
{
    rScRanges.RemoveAll();
    for( const auto& rXclRange : rXclRanges )
    {
        ScRange aScRange( ScAddress::UNINITIALIZED );
        if( ConvertRange( aScRange, rXclRange, nScTab, nScTab, bWarn ) )
            rScRanges.push_back( aScRange );
    }
}
 
// String->EditEngine conversion ==============================================
 
namespace {
 
std::unique_ptr<EditTextObject> lclCreateTextObject( const XclImpRoot& rRoot,
        const XclImpString& rString, XclFontItemType eType, sal_uInt16 nXFIndex )
{
    std::unique_ptr<EditTextObject> pTextObj;
 
    const XclImpXFBuffer& rXFBuffer = rRoot.GetXFBuffer();
    const XclImpFont* pFirstFont = rXFBuffer.GetFont( nXFIndex );
    bool bFirstEscaped = pFirstFont && pFirstFont->HasEscapement();
 
    if( rString.IsRich() || bFirstEscaped )
    {
        const XclImpFontBuffer& rFontBuffer = rRoot.GetFontBuffer();
        const XclFormatRunVec& rFormats = rString.GetFormats();
 
        ScEditEngineDefaulter& rEE = rRoot.GetEditEngine();
        rEE.SetTextCurrentDefaults( rString.GetText() );
 
        SfxItemSet aItemSet( rEE.GetEmptyItemSet() );
        if( bFirstEscaped )
            rFontBuffer.FillToItemSet( aItemSet, eType, rXFBuffer.GetFontIndex( nXFIndex ) );
        ESelection aSelection;
 
        XclFormatRun aNextRun;
        XclFormatRunVec::const_iterator aIt = rFormats.begin();
        XclFormatRunVec::const_iterator aEnd = rFormats.end();
 
        if( aIt != aEnd )
            aNextRun = *aIt++;
        else
            aNextRun.mnChar = 0xFFFF;
 
        sal_Int32 nLen = rString.GetText().getLength();
        for( sal_Int32 nChar = 0; nChar < nLen; ++nChar )
        {
            // reached new different formatted text portion
            if( nChar >= aNextRun.mnChar )
            {
                // send items to edit engine
                rEE.QuickSetAttribs( aItemSet, aSelection );
 
                // start new item set
                aItemSet.ClearItem();
                rFontBuffer.FillToItemSet( aItemSet, eType, aNextRun.mnFontIdx );
 
                // read new formatting information
                if( aIt != aEnd )
                    aNextRun = *aIt++;
                else
                    aNextRun.mnChar = 0xFFFF;
 
                // reset selection start to current position
                aSelection.nStartPara = aSelection.nEndPara;
                aSelection.nStartPos = aSelection.nEndPos;
            }
 
            // set end of selection to current position
            if( rString.GetText()[ nChar ] == '\n' )
            {
                ++aSelection.nEndPara;
                aSelection.nEndPos = 0;
            }
            else
                ++aSelection.nEndPos;
        }
 
        // send items of last text portion to edit engine
        rEE.QuickSetAttribs( aItemSet, aSelection );
 
        pTextObj = rEE.CreateTextObject();
    }
 
    return pTextObj;
}
 
} // namespace
 
std::unique_ptr<EditTextObject> XclImpStringHelper::CreateTextObject(
        const XclImpRoot& rRoot, const XclImpString& rString )
{
    return lclCreateTextObject( rRoot, rString, XclFontItemType::Editeng, 0 );
}
 
void XclImpStringHelper::SetToDocument(
        ScDocumentImport& rDoc, const ScAddress& rPos, const XclImpRoot& rRoot,
        const XclImpString& rString, sal_uInt16 nXFIndex )
{
    if (rString.GetText().isEmpty())
        return;
 
    ::std::unique_ptr< EditTextObject > pTextObj( lclCreateTextObject( rRoot, rString, XclFontItemType::Editeng, nXFIndex ) );
 
    if (pTextObj)
    {
        rDoc.setEditCell(rPos, std::move(pTextObj));
    }
    else
    {
        const OUString& aStr = rString.GetText();
        if (aStr.indexOf('\n') != -1 || aStr.indexOf('\r') != -1)
        {
            const XclImpXFBuffer& rXFBuffer = rRoot.GetXFBuffer();
            const XclImpXF* pXF = rXFBuffer.GetXF( nXFIndex );
            bool bSingleLine = pXF ? !pXF->GetLineBreak() : false;
 
            // Multiline content.
            ScFieldEditEngine& rEngine = rDoc.getDoc().GetEditEngine();
            rEngine.SetSingleLine(bSingleLine);
            rEngine.SetTextCurrentDefaults(aStr);
            rDoc.setEditCell(rPos, rEngine.CreateTextObject());
            rEngine.SetSingleLine(false);
        }
        else
        {
            // Normal text cell.
            rDoc.setStringCell(rPos, aStr);
        }
    }
}
 
// Header/footer conversion ===================================================
 
XclImpHFConverter::XclImpHFPortionInfo::XclImpHFPortionInfo() :
    mnHeight( 0 ),
    mnMaxLineHt( 0 )
{
    maSel.nStartPara = maSel.nEndPara = 0;
    maSel.nStartPos = maSel.nEndPos = 0;
}
 
XclImpHFConverter::XclImpHFConverter( const XclImpRoot& rRoot ) :
    XclImpRoot( rRoot ),
    mrEE( rRoot.GetHFEditEngine() ),
    mxFontData( new XclFontData ),
    meCurrObj( EXC_HF_CENTER )
{
}
 
XclImpHFConverter::~XclImpHFConverter()
{
}
 
void XclImpHFConverter::ParseString( const OUString& rHFString )
{
    // edit engine objects
    mrEE.SetText( OUString() );
    maInfos.clear();
    maInfos.resize( EXC_HF_PORTION_COUNT );
    meCurrObj = EXC_HF_CENTER;
 
    // parser temporaries
    maCurrText.truncate();
    OUStringBuffer aReadFont;   // current font name
    OUStringBuffer aReadStyle;  // current font style
    sal_uInt16 nReadHeight = 0; // current font height
    ResetFontData();
 
    /** State of the parser. */
    enum XclHFParserState
    {
        xlPSText,           /// Read text, search for functions.
        xlPSFunc,           /// Read function (token following a '&').
        xlPSFont,           /// Read font name ('&' is followed by '"', reads until next '"' or ',').
        xlPSFontStyle,      /// Read font style name (font part after ',', reads until next '"').
        xlPSHeight          /// Read font height ('&' is followed by num. digits, reads until non-digit).
    } eState = xlPSText;
 
    const sal_Unicode* pChar = rHFString.getStr();
    const sal_Unicode* pNull = pChar + rHFString.getLength(); // pointer to terminating null char
    while( *pChar )
    {
        switch( eState )
        {
 
// --- read text character ---
 
            case xlPSText:
            {
                switch( *pChar )
                {
                    case '&':           // new command
                        InsertText();
                        eState = xlPSFunc;
                    break;
                    case '\n':          // line break
                        InsertText();
                        InsertLineBreak();
                    break;
                    default:
                        maCurrText.append(OUStringChar(*pChar));
                }
            }
            break;
 
// --- read control sequence ---
 
            case xlPSFunc:
            {
                eState = xlPSText;
                switch( *pChar )
                {
                    case '&':   maCurrText.append("&");  break;  // the '&' character
 
                    case 'L':   SetNewPortion( EXC_HF_LEFT );   break;  // Left portion
                    case 'C':   SetNewPortion( EXC_HF_CENTER ); break;  // Center portion
                    case 'R':   SetNewPortion( EXC_HF_RIGHT );  break;  // Right portion
 
                    case 'P':   InsertField( SvxFieldItem( SvxPageField(), EE_FEATURE_FIELD ) );      break;  // page
                    case 'N':   InsertField( SvxFieldItem( SvxPagesField(), EE_FEATURE_FIELD ) );     break;  // page count
                    case 'D':   InsertField( SvxFieldItem( SvxDateField(), EE_FEATURE_FIELD ) );      break;  // date
                    case 'T':   InsertField( SvxFieldItem( SvxTimeField(), EE_FEATURE_FIELD ) );      break;  // time
                    case 'A':   InsertField( SvxFieldItem( SvxTableField(), EE_FEATURE_FIELD ) );     break;  // table name
 
                    case 'Z':           // file path
                        InsertField( SvxFieldItem( SvxExtFileField(), EE_FEATURE_FIELD ) );   // convert to full name
                        if( (pNull - pChar >= 2) && (*(pChar + 1) == '&') && (*(pChar + 2) == 'F') )
                        {
                            // &Z&F found - ignore the &F part
                            pChar += 2;
                        }
                    break;
                    case 'F':           // file name
                        InsertField( SvxFieldItem( SvxExtFileField( OUString(), SvxFileType::Var, SvxFileFormat::NameAndExt ), EE_FEATURE_FIELD ) );
                    break;
 
                    case 'U':           // underline
                        SetAttribs();
                        mxFontData->mnUnderline = (mxFontData->mnUnderline == EXC_FONTUNDERL_SINGLE) ?
                            EXC_FONTUNDERL_NONE : EXC_FONTUNDERL_SINGLE;
                    break;
                    case 'E':           // double underline
                        SetAttribs();
                        mxFontData->mnUnderline = (mxFontData->mnUnderline == EXC_FONTUNDERL_DOUBLE) ?
                            EXC_FONTUNDERL_NONE : EXC_FONTUNDERL_DOUBLE;
                    break;
                    case 'S':           // strikeout
                        SetAttribs();
                        mxFontData->mbStrikeout = !mxFontData->mbStrikeout;
                    break;
                    case 'X':           // superscript
                        SetAttribs();
                        mxFontData->mnEscapem = (mxFontData->mnEscapem == EXC_FONTESC_SUPER) ?
                            EXC_FONTESC_NONE : EXC_FONTESC_SUPER;
                    break;
                    case 'Y':           // subscript
                        SetAttribs();
                        mxFontData->mnEscapem = (mxFontData->mnEscapem == EXC_FONTESC_SUB) ?
                            EXC_FONTESC_NONE : EXC_FONTESC_SUB;
                    break;
 
                    case '\"':          // font name
                        aReadFont.setLength(0);
                        aReadStyle.setLength(0);
                        eState = xlPSFont;
                    break;
                    default:
                        if( ('0' <= *pChar) && (*pChar <= '9') )    // font size
                        {
                            nReadHeight = *pChar - '0';
                            eState = xlPSHeight;
                        }
                }
            }
            break;
 
// --- read font name ---
 
            case xlPSFont:
            {
                switch( *pChar )
                {
                    case '\"':
                        --pChar;
                        [[fallthrough]];
                    case ',':
                        eState = xlPSFontStyle;
                    break;
                    default:
                        aReadFont.append(*pChar);
                }
            }
            break;
 
// --- read font style ---
 
            case xlPSFontStyle:
            {
                switch( *pChar )
                {
                    case '\"':
                        SetAttribs();
                        if( !aReadFont.isEmpty() )
                            mxFontData->maName = aReadFont.toString();
                        mxFontData->maStyle = aReadStyle.toString();
                        eState = xlPSText;
                    break;
                    default:
                        aReadStyle.append(*pChar);
                }
            }
            break;
 
// --- read font height ---
 
            case xlPSHeight:
            {
                if( ('0' <= *pChar) && (*pChar <= '9') )
                {
                    if( nReadHeight != 0xFFFF )
                    {
                        nReadHeight *= 10;
                        nReadHeight += (*pChar - '0');
                        if( nReadHeight > 1600 )    // max 1600pt = 32000twips
                            nReadHeight = 0xFFFF;
                    }
                }
                else
                {
                    if( (nReadHeight != 0) && (nReadHeight != 0xFFFF) )
                    {
                        SetAttribs();
                        mxFontData->mnHeight = nReadHeight * 20;
                    }
                    --pChar;
                    eState = xlPSText;
                }
            }
            break;
        }
        ++pChar;
    }
 
    // finalize
    CreateCurrObject();
    maInfos[ EXC_HF_LEFT   ].mnHeight += GetMaxLineHeight( EXC_HF_LEFT );
    maInfos[ EXC_HF_CENTER ].mnHeight += GetMaxLineHeight( EXC_HF_CENTER );
    maInfos[ EXC_HF_RIGHT  ].mnHeight += GetMaxLineHeight( EXC_HF_RIGHT );
}
 
void XclImpHFConverter::FillToItemSet( SfxItemSet& rItemSet, sal_uInt16 nWhichId ) const
{
    ScPageHFItem aHFItem( nWhichId );
    if( maInfos[ EXC_HF_LEFT ].mxObj )
        aHFItem.SetLeftArea( *maInfos[ EXC_HF_LEFT ].mxObj );
    if( maInfos[ EXC_HF_CENTER ].mxObj )
        aHFItem.SetCenterArea( *maInfos[ EXC_HF_CENTER ].mxObj );
    if( maInfos[ EXC_HF_RIGHT ].mxObj )
        aHFItem.SetRightArea( *maInfos[ EXC_HF_RIGHT ].mxObj );
    rItemSet.Put( aHFItem );
}
 
sal_Int32 XclImpHFConverter::GetTotalHeight() const
{
    return ::std::max( maInfos[ EXC_HF_LEFT ].mnHeight,
        ::std::max( maInfos[ EXC_HF_CENTER ].mnHeight, maInfos[ EXC_HF_RIGHT ].mnHeight ) );
}
 
// private --------------------------------------------------------------------
 
sal_uInt16 XclImpHFConverter::GetMaxLineHeight( XclImpHFPortion ePortion ) const
{
    sal_uInt16 nMaxHt = maInfos[ ePortion ].mnMaxLineHt;
    return (nMaxHt == 0) ? mxFontData->mnHeight : nMaxHt;
}
 
void XclImpHFConverter::UpdateMaxLineHeight( XclImpHFPortion ePortion )
{
    sal_uInt16& rnMaxHt = maInfos[ ePortion ].mnMaxLineHt;
    rnMaxHt = ::std::max( rnMaxHt, mxFontData->mnHeight );
}
 
void XclImpHFConverter::UpdateCurrMaxLineHeight()
{
    UpdateMaxLineHeight( meCurrObj );
}
 
void XclImpHFConverter::SetAttribs()
{
    ESelection& rSel = GetCurrSel();
    if( (rSel.nStartPara != rSel.nEndPara) || (rSel.nStartPos != rSel.nEndPos) )
    {
        SfxItemSet aItemSet( mrEE.GetEmptyItemSet() );
        XclImpFont aFont( GetRoot(), *mxFontData );
        aFont.FillToItemSet( aItemSet, XclFontItemType::HeaderFooter );
        mrEE.QuickSetAttribs( aItemSet, rSel );
        rSel.nStartPara = rSel.nEndPara;
        rSel.nStartPos = rSel.nEndPos;
    }
}
 
void XclImpHFConverter::ResetFontData()
{
    if( const XclImpFont* pFirstFont = GetFontBuffer().GetFont( EXC_FONT_APP ) )
        *mxFontData = pFirstFont->GetFontData();
    else
    {
        mxFontData->Clear();
        mxFontData->mnHeight = 200;
    }
}
 
void XclImpHFConverter::InsertText()
{
    if( !maCurrText.isEmpty() )
    {
        ESelection& rSel = GetCurrSel();
        OUString sString(maCurrText.makeStringAndClear());
        mrEE.QuickInsertText( sString, ESelection( rSel.nEndPara, rSel.nEndPos, rSel.nEndPara, rSel.nEndPos ) );
        rSel.nEndPos = rSel.nEndPos + sString.getLength();
        UpdateCurrMaxLineHeight();
    }
}
 
void XclImpHFConverter::InsertField( const SvxFieldItem& rFieldItem )
{
    ESelection& rSel = GetCurrSel();
    mrEE.QuickInsertField( rFieldItem, ESelection( rSel.nEndPara, rSel.nEndPos, rSel.nEndPara, rSel.nEndPos ) );
    ++rSel.nEndPos;
    UpdateCurrMaxLineHeight();
}
 
void XclImpHFConverter::InsertLineBreak()
{
    ESelection& rSel = GetCurrSel();
    mrEE.QuickInsertText( OUString('\n'), ESelection( rSel.nEndPara, rSel.nEndPos, rSel.nEndPara, rSel.nEndPos ) );
    ++rSel.nEndPara;
    rSel.nEndPos = 0;
    GetCurrInfo().mnHeight += GetMaxLineHeight( meCurrObj );
    GetCurrInfo().mnMaxLineHt = 0;
}
 
void XclImpHFConverter::CreateCurrObject()
{
    InsertText();
    SetAttribs();
    GetCurrObj() = mrEE.CreateTextObject();
}
 
void XclImpHFConverter::SetNewPortion( XclImpHFPortion eNew )
{
    if( eNew != meCurrObj )
    {
        CreateCurrObject();
        meCurrObj = eNew;
        if( GetCurrObj() )
            mrEE.SetText( *GetCurrObj() );
        else
            mrEE.SetText( OUString() );
        ResetFontData();
    }
}
 
// URL conversion =============================================================
 
namespace {
 
void lclAppendUrlChar( OUString& rUrl, sal_Unicode cChar )
{
    // encode special characters
    switch( cChar )
    {
        case '#':   rUrl += "%23";  break;
        case '%':   rUrl += "%25";  break;
        default:    rUrl += OUStringChar( cChar );
    }
}
 
} // namespace
 
void XclImpUrlHelper::DecodeUrl(
        OUString& rUrl, OUString& rTabName, bool& rbSameWb,
        const XclImpRoot& rRoot, const OUString& rEncodedUrl )
{
    enum
    {
        xlUrlInit,              /// Initial state, read string mode character.
        xlUrlPath,              /// Read URL path.
        xlUrlFileName,          /// Read file name.
        xlUrlSheetName,         /// Read sheet name.
        xlUrlRaw                /// Raw mode. No control characters will occur.
    } eState = xlUrlInit;
 
    bool bEncoded = true;
    rbSameWb = false;
 
    sal_Unicode cCurrDrive = 0;
    OUString aDosBase( INetURLObject( rRoot.GetBasePath() ).getFSysPath( FSysStyle::Dos ) );
    if (!aDosBase.isEmpty() && aDosBase.match(":\\", 1))
        cCurrDrive = aDosBase[0];
 
    const sal_Unicode* pChar = rEncodedUrl.getStr();
    while( *pChar )
    {
        switch( eState )
        {
 
// --- first character ---
 
            case xlUrlInit:
            {
                switch( *pChar )
                {
                    case EXC_URLSTART_ENCODED:
                        eState = xlUrlPath;
                    break;
                    case EXC_URLSTART_SELF:
                    case EXC_URLSTART_SELFENCODED:
                        rbSameWb = true;
                        eState = xlUrlSheetName;
                    break;
                    case '[':
                        bEncoded = false;
                        eState = xlUrlFileName;
                    break;
                    default:
                        bEncoded = false;
                        lclAppendUrlChar( rUrl, *pChar );
                        eState = xlUrlPath;
                }
            }
            break;
 
// --- URL path ---
 
            case xlUrlPath:
            {
                switch( *pChar )
                {
                    case EXC_URL_DOSDRIVE:
                    {
                        if( *(pChar + 1) )
                        {
                            ++pChar;
                            if( *pChar == '@' )
                                rUrl += "\\\\";
                            else
                            {
                                lclAppendUrlChar( rUrl, *pChar );
                                rUrl += ":\\";
                            }
                        }
                        else
                            rUrl += "<NULL-DRIVE!>";
                    }
                    break;
                    case EXC_URL_DRIVEROOT:
                        if( cCurrDrive )
                        {
                            lclAppendUrlChar( rUrl, cCurrDrive );
                            rUrl += ":";
                        }
                        [[fallthrough]];
                    case EXC_URL_SUBDIR:
                        if( bEncoded )
                            rUrl += "\\";
                        else    // control character in raw name -> DDE link
                        {
                            rUrl += OUStringChar(EXC_DDE_DELIM);
                            eState = xlUrlRaw;
                        }
                    break;
                    case EXC_URL_PARENTDIR:
                        rUrl += "..\\";
                    break;
                    case EXC_URL_RAW:
                    {
                        if( *(pChar + 1) )
                        {
                            sal_Int32 nLen = *++pChar;
                            for( sal_Int32 nChar = 0; (nChar < nLen) && *(pChar + 1); ++nChar )
                                lclAppendUrlChar( rUrl, *++pChar );
//                            rUrl.Append( ':' );
                        }
                    }
                    break;
                    case '[':
                        eState = xlUrlFileName;
                    break;
                    default:
                        lclAppendUrlChar( rUrl, *pChar );
                }
            }
            break;
 
// --- file name ---
 
            case xlUrlFileName:
            {
                switch( *pChar )
                {
                    case ']':   eState = xlUrlSheetName;    break;
                    default:    lclAppendUrlChar( rUrl, *pChar );
                }
            }
            break;
 
// --- sheet name ---
 
            case xlUrlSheetName:
                rTabName += OUStringChar( *pChar );
            break;
 
// --- raw read mode ---
 
            case xlUrlRaw:
                lclAppendUrlChar( rUrl, *pChar );
            break;
        }
 
        ++pChar;
    }
}
 
void XclImpUrlHelper::DecodeUrl(
    OUString& rUrl, bool& rbSameWb, const XclImpRoot& rRoot, const OUString& rEncodedUrl )
{
    OUString aTabName;
    OUString aUrl;
    DecodeUrl( aUrl, aTabName, rbSameWb, rRoot, rEncodedUrl );
    rUrl = aUrl;
    OSL_ENSURE( aTabName.isEmpty(), "XclImpUrlHelper::DecodeUrl - sheet name ignored" );
}
 
bool XclImpUrlHelper::DecodeLink( OUString& rApplic, OUString& rTopic, std::u16string_view aEncUrl )
{
    size_t nPos = aEncUrl.find( EXC_DDE_DELIM );
    if( nPos != std::u16string_view::npos && (nPos > 0) && (nPos + 1 < aEncUrl.size()) )
    {
        rApplic = aEncUrl.substr( 0, nPos );
        rTopic = aEncUrl.substr( nPos + 1 );
        return true;
    }
    return false;
}
 
// Cached Values ==============================================================
 
XclImpCachedValue::XclImpCachedValue( XclImpStream& rStrm ) :
    mfValue( 0.0 ),
    mnBoolErr( 0 )
{
    mnType = rStrm.ReaduInt8();
    switch( mnType )
    {
        case EXC_CACHEDVAL_EMPTY:
            rStrm.Ignore( 8 );
        break;
        case EXC_CACHEDVAL_DOUBLE:
            mfValue = rStrm.ReadDouble();
        break;
        case EXC_CACHEDVAL_STRING:
            maStr = rStrm.ReadUniString();
        break;
        case EXC_CACHEDVAL_BOOL:
        case EXC_CACHEDVAL_ERROR:
        {
            double fVal;
            mnBoolErr = rStrm.ReaduInt8();
            rStrm.Ignore( 7 );
 
            std::unique_ptr<ScTokenArray> pScTokArr = rStrm.GetRoot().GetOldFmlaConverter().GetBoolErr(
                XclTools::ErrorToEnum( fVal, mnType == EXC_CACHEDVAL_ERROR, mnBoolErr ) );
            if( pScTokArr )
                mxTokArr = std::move( pScTokArr );
        }
        break;
        default:
            OSL_FAIL( "XclImpCachedValue::XclImpCachedValue - unknown data type" );
    }
}
 
XclImpCachedValue::~XclImpCachedValue()
{
}
 
FormulaError XclImpCachedValue::GetScError() const
{
    return (mnType == EXC_CACHEDVAL_ERROR) ? XclTools::GetScErrorCode( mnBoolErr ) : FormulaError::NONE;
}
 
// Matrix Cached Values ==============================================================
 
XclImpCachedMatrix::XclImpCachedMatrix( XclImpStream& rStrm ) :
    mnScCols( 0 ),
    mnScRows( 0 )
{
    mnScCols = rStrm.ReaduInt8();
    mnScRows = rStrm.ReaduInt16();
 
    if( rStrm.GetRoot().GetBiff() <= EXC_BIFF5 )
    {
        // in BIFF2-BIFF7: 256 columns represented by 0 columns
        if( mnScCols == 0 )
            mnScCols = 256;
    }
    else
    {
        // in BIFF8: columns and rows decreased by 1
        ++mnScCols;
        ++mnScRows;
    }
 
    //assuming worst case scenario of unknown types
    const size_t nMinRecordSize = 1;
    const size_t nMaxRows = rStrm.GetRecLeft() / (nMinRecordSize * mnScCols);
    if (mnScRows > nMaxRows)
    {
        SAL_WARN("sc", "Parsing error: " << nMaxRows <<
                 " max possible rows, but " << mnScRows << " claimed, truncating");
        mnScRows = nMaxRows;
    }
 
    for( SCSIZE nScRow = 0; nScRow < mnScRows; ++nScRow )
        for( SCSIZE nScCol = 0; nScCol < mnScCols; ++nScCol )
            maValueList.push_back( std::make_unique<XclImpCachedValue>( rStrm ) );
}
 
XclImpCachedMatrix::~XclImpCachedMatrix()
{
}
 
ScMatrixRef XclImpCachedMatrix::CreateScMatrix( svl::SharedStringPool& rPool ) const
{
    ScMatrixRef xScMatrix;
    OSL_ENSURE( mnScCols * mnScRows == maValueList.size(), "XclImpCachedMatrix::CreateScMatrix - element count mismatch" );
    if( mnScCols && mnScRows && static_cast< sal_uLong >( mnScCols * mnScRows ) <= maValueList.size() )
    {
        xScMatrix = new ScMatrix(mnScCols, mnScRows, 0.0);
        XclImpValueList::const_iterator itValue = maValueList.begin();
        for( SCSIZE nScRow = 0; nScRow < mnScRows; ++nScRow )
        {
            for( SCSIZE nScCol = 0; nScCol < mnScCols; ++nScCol )
            {
                switch( (*itValue)->GetType() )
                {
                    case EXC_CACHEDVAL_EMPTY:
                        // Excel shows 0.0 here, not an empty cell
                        xScMatrix->PutEmpty( nScCol, nScRow );
                    break;
                    case EXC_CACHEDVAL_DOUBLE:
                        xScMatrix->PutDouble( (*itValue)->GetValue(), nScCol, nScRow );
                    break;
                    case EXC_CACHEDVAL_STRING:
                        xScMatrix->PutString(rPool.intern((*itValue)->GetString()), nScCol, nScRow);
                    break;
                    case EXC_CACHEDVAL_BOOL:
                        xScMatrix->PutBoolean( (*itValue)->GetBool(), nScCol, nScRow );
                    break;
                    case EXC_CACHEDVAL_ERROR:
                        xScMatrix->PutError( (*itValue)->GetScError(), nScCol, nScRow );
                    break;
                    default:
                        OSL_FAIL( "XclImpCachedMatrix::CreateScMatrix - unknown value type" );
                        xScMatrix->PutEmpty( nScCol, nScRow );
                }
                ++itValue;
            }
        }
    }
    return xScMatrix;
}
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

V530 The return value of function 'truncate' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.