/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the Collabora Office 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 <sheetdatacontext.hxx>

#include <oox/core/xmlfilterbase.hxx>
#include <oox/helper/attributelist.hxx>
#include <oox/helper/binaryinputstream.hxx>
#include <oox/token/namespaces.hxx>
#include <oox/token/tokens.hxx>
#include <addressconverter.hxx>
#include <biffhelper.hxx>
#include <formulaparser.hxx>
#include <richstringcontext.hxx>
#include <sal/log.hxx>
#include <o3tl/string_view.hxx>

namespace oox::xls {

using ::oox::core::ContextHandlerRef;

namespace {

// record constants -----------------------------------------------------------

const sal_uInt32 BIFF12_CELL_SHOWPHONETIC   = 0x01000000;

const sal_uInt8 BIFF12_DATATABLE_ROW        = 0x01;
const sal_uInt8 BIFF12_DATATABLE_2D         = 0x02;
const sal_uInt8 BIFF12_DATATABLE_REF1DEL    = 0x04;
const sal_uInt8 BIFF12_DATATABLE_REF2DEL    = 0x08;

const sal_uInt16 BIFF12_ROW_THICKTOP        = 0x0001;
const sal_uInt16 BIFF12_ROW_THICKBOTTOM     = 0x0002;
const sal_uInt16 BIFF12_ROW_COLLAPSED       = 0x0800;
const sal_uInt16 BIFF12_ROW_HIDDEN          = 0x1000;
const sal_uInt16 BIFF12_ROW_CUSTOMHEIGHT    = 0x2000;
const sal_uInt16 BIFF12_ROW_CUSTOMFORMAT    = 0x4000;
const sal_uInt8 BIFF12_ROW_SHOWPHONETIC     = 0x01;

} // namespace

SheetDataContext::SheetDataContext( WorksheetFragmentBase& rFragment ) :
    WorksheetContextBase( rFragment ),
    mrAddressConv( rFragment.getAddressConverter() ),
    mrSheetData( rFragment.getSheetData() ),
    mnSheet( rFragment.getSheetIndex() ),
    mbHasFormula( false ),
    mbValidRange( false ),
    mbCalcAlways( false ),
    mnRow( -1 ),
    mnCol( -1 )
{
    SAL_INFO( "sc.filter",  "start safe sheet data context - unlock" );
    mxFormulaParser.reset(rFragment.createFormulaParser());
}

SheetDataContext::~SheetDataContext()
{
    SAL_INFO( "sc.filter",  "end safe sheet data context - relock" );
}

ContextHandlerRef SheetDataContext::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs )
{
    switch( getCurrentElement() )
    {
        case XLS_TOKEN( sheetData ):
            if( nElement == XLS_TOKEN( row ) ) { importRow( rAttribs ); return this; }
        break;

        case XLS_TOKEN( row ):
            // do not process cell elements with invalid (out-of-range) address
            if( nElement == XLS_TOKEN( c ) && importCell( rAttribs ) )
                return this;
        break;

        case XLS_TOKEN( c ):
            switch( nElement )
            {
                case XLS_TOKEN( is ):
                    mxInlineStr = std::make_shared<RichString>();
                    return new RichStringContext( *this, mxInlineStr );
                case XLS_TOKEN( v ):
                    return this;    // characters contain cell value
                case XLS_TOKEN( f ):
                    importFormula( rAttribs );
                    return this;    // characters contain formula string
            }
        break;
    }
    return nullptr;
}

void SheetDataContext::onCharacters( const OUString& rChars )
{
    switch( getCurrentElement() )
    {
        case XLS_TOKEN( v ):
            maCellValue = rChars;
        break;
        case XLS_TOKEN( f ):
            if( maFmlaData.mnFormulaType != XML_TOKEN_INVALID )
            {
                maFormulaStr = rChars;
            }
        break;
    }
}

void SheetDataContext::onEndElement()
{
    if( getCurrentElement() != XLS_TOKEN( c ) )
        return;

    // try to create a formula cell
    if( mbHasFormula ) switch( maFmlaData.mnFormulaType )
    {
        // will buffer formulas but need to
        // a) need to set format first
        // :/
        case XML_normal:
            // An empty <f/> without ca is a standalone broken formula and
            // should be skipped to avoid creating a formula cell with no
            // tokens.
            // An empty <f/> with ca="1" is a matrix-reference placeholder
            // for an array formula whose master is a different cell; the
            // master will create the matrix cells via setMatrixCells.
            if (!maFormulaStr.isEmpty() || mbCalcAlways)
            {
                setCellFormula(maCellData.maCellAddr, maFormulaStr, mbCellMetadata);
                mrSheetData.setCellFormat(maCellData);

                // If a number cell has some preloaded value, stick it into the buffer
                // but do this only for real cell formulas (not array, shared etc.)
                if (!maCellValue.isEmpty())
                    setCellFormulaValue(maCellData.maCellAddr, maCellValue, maCellData.mnCellType);
            }
            else
                mbHasFormula = false;
            break;

        case XML_shared:
            if( maFmlaData.mnSharedId >= 0 )
            {
                if( mbValidRange && maFmlaData.isValidSharedRef( maCellData.maCellAddr ) )
                    createSharedFormulaMapEntry(maCellData.maCellAddr, maFmlaData.mnSharedId, maFormulaStr);

                setCellFormula(maCellData.maCellAddr, maFmlaData.mnSharedId, maCellValue, maCellData.mnCellType,
                               mbCellMetadata);
                mrSheetData.setCellFormat( maCellData );
            }
            else
                // no success, set plain cell value and formatting below
                mbHasFormula = false;
        break;
        case XML_array:
            if( mbValidRange && maFmlaData.isValidArrayRef( maCellData.maCellAddr ) )
            {
                // A cached error on the array master signals that the file
                // was saved while the dynamic array's spill range was blocked.
                // The OOXML spec has no #SPILL! cell error literal, so write
                // #VALUE! and reclassify via valueMetadata.
                // Plumbing this through keeps the import path from writing
                // reference cells over the blocker.
                const bool bCachedSpill
                    = maCellData.mnCellType == XML_e
                      && mbCellMetadata
                      && maCellValue == u"#VALUE!";
                setCellArrayFormula(maFmlaData.maFormulaRef, maCellData.maCellAddr, maFormulaStr,
                                    bCachedSpill, mbCellMetadata);
            }
            // set cell formatting, but do not set result as cell value
            mrSheetData.setBlankCell( maCellData );
        break;
        case XML_dataTable:
            if( mbValidRange )
                mrSheetData.createTableOperation( maFmlaData.maFormulaRef, maTableData );
            // set cell formatting, but do not set result as cell value
            mrSheetData.setBlankCell( maCellData );
        break;
        default:
            OSL_ENSURE( maFmlaData.mnFormulaType == XML_TOKEN_INVALID, "SheetDataContext::onEndElement - unknown formula type" );
            mbHasFormula = false;
    }

    if( mbHasFormula )
        return;

    // no formula created: try to set the cell value
    if( !maCellValue.isEmpty() ) switch( maCellData.mnCellType )
    {
        case XML_n:
            mrSheetData.setValueCell( maCellData, maCellValue.toDouble() );
        break;
        case XML_b:
            {
                // Some generators may write true or false instead of 1 or 0.
                /* XXX NOTE: PivotCacheItem::readBool() may suffer from this as
                 * well, but for now let's assume that software writing this
                 * here wrong won't write pivot caches at all.. */
                bool bValue = (maCellValue.toDouble() != 0.0);
                if (!bValue && maCellValue.equalsIgnoreAsciiCase(u"true"))
                    bValue = true;
                mrSheetData.setBooleanCell( maCellData, bValue );
            }
        break;
        case XML_e:
            mrSheetData.setErrorCell( maCellData, maCellValue );
        break;
        case XML_str:
            mrSheetData.setStringCell( maCellData, maCellValue );
        break;
        case XML_s:
            mrSheetData.setStringCell( maCellData, maCellValue.toInt32() );
        break;
        case XML_d:
            mrSheetData.setDateCell( maCellData, maCellValue );
        break;
    }
    else if( (maCellData.mnCellType == XML_inlineStr) && mxInlineStr )
    {
        mxInlineStr->finalizeImport(*this);
        mrSheetData.setStringCell( maCellData, mxInlineStr );
    }
    else
    {
        // empty cell, update cell type
        maCellData.mnCellType = XML_TOKEN_INVALID;
        mrSheetData.setBlankCell( maCellData );
    }
}

ContextHandlerRef SheetDataContext::onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm )
{
    switch( getCurrentElement() )
    {
        case BIFF12_ID_SHEETDATA:
            if( nRecId == BIFF12_ID_ROW ) { importRow( rStrm ); return this; }
        break;

        case BIFF12_ID_ROW:
            switch( nRecId )
            {
                case BIFF12_ID_ARRAY:           importArray( rStrm );                           break;
                case BIFF12_ID_CELL_BOOL:       importCellBool( rStrm, CELLTYPE_VALUE );        break;
                case BIFF12_ID_CELL_BLANK:      importCellBlank( rStrm, CELLTYPE_VALUE );       break;
                case BIFF12_ID_CELL_DOUBLE:     importCellDouble( rStrm, CELLTYPE_VALUE );      break;
                case BIFF12_ID_CELL_ERROR:      importCellError( rStrm, CELLTYPE_VALUE );       break;
                case BIFF12_ID_CELL_RK:         importCellRk( rStrm, CELLTYPE_VALUE );          break;
                case BIFF12_ID_CELL_RSTRING:    importCellRString( rStrm, CELLTYPE_VALUE );     break;
                case BIFF12_ID_CELL_SI:         importCellSi( rStrm, CELLTYPE_VALUE );          break;
                case BIFF12_ID_CELL_STRING:     importCellString( rStrm, CELLTYPE_VALUE );      break;
                case BIFF12_ID_DATATABLE:       importDataTable( rStrm );                       break;
                case BIFF12_ID_FORMULA_BOOL:    importCellBool( rStrm, CELLTYPE_FORMULA );      break;
                case BIFF12_ID_FORMULA_DOUBLE:  importCellDouble( rStrm, CELLTYPE_FORMULA );    break;
                case BIFF12_ID_FORMULA_ERROR:   importCellError( rStrm, CELLTYPE_FORMULA );     break;
                case BIFF12_ID_FORMULA_STRING:  importCellString( rStrm, CELLTYPE_FORMULA );    break;
                case BIFF12_ID_MULTCELL_BOOL:   importCellBool( rStrm, CELLTYPE_MULTI );        break;
                case BIFF12_ID_MULTCELL_BLANK:  importCellBlank( rStrm, CELLTYPE_MULTI );       break;
                case BIFF12_ID_MULTCELL_DOUBLE: importCellDouble( rStrm, CELLTYPE_MULTI );      break;
                case BIFF12_ID_MULTCELL_ERROR:  importCellError( rStrm, CELLTYPE_MULTI );       break;
                case BIFF12_ID_MULTCELL_RK:     importCellRk( rStrm, CELLTYPE_MULTI );          break;
                case BIFF12_ID_MULTCELL_RSTRING:importCellRString( rStrm, CELLTYPE_MULTI );     break;
                case BIFF12_ID_MULTCELL_SI:     importCellSi( rStrm, CELLTYPE_MULTI );          break;
                case BIFF12_ID_MULTCELL_STRING: importCellString( rStrm, CELLTYPE_MULTI );      break;
                case BIFF12_ID_SHAREDFMLA:      importSharedFmla( rStrm );                      break;
            }
        break;
    }
    return nullptr;
}

// private --------------------------------------------------------------------

void SheetDataContext::importRow( const AttributeList& rAttribs )
{
    RowModel aModel;
    sal_Int32 nRow = rAttribs.getInteger( XML_r, -1 ); // 1-based row index
    if(nRow != -1)
    {
        aModel.mnRow = nRow;
        mnRow = nRow-1; // to 0-based row index.
    }
    else
        aModel.mnRow = (++mnRow + 1);   // increment 0-based row index, to 1-based model row
    mrAddressConv.checkRow( mnRow, true);
    mnCol = -1;

    aModel.mfHeight       = rAttribs.getDouble( XML_ht, -1.0 );
    aModel.mnXfId = rAttribs.getInteger(XML_s, 0); // default style index is 0
    aModel.mnLevel        = rAttribs.getInteger( XML_outlineLevel, 0 );
    aModel.mbCustomHeight = rAttribs.getBool( XML_customHeight, false );
    aModel.mbCustomFormat = rAttribs.getBool( XML_customFormat, false );
    aModel.mbShowPhonetic = rAttribs.getBool( XML_ph, false );
    aModel.mbHidden       = rAttribs.getBool( XML_hidden, false );
    aModel.mbCollapsed    = rAttribs.getBool( XML_collapsed, false );
    aModel.mbThickTop     = rAttribs.getBool( XML_thickTop, false );
    aModel.mbThickBottom  = rAttribs.getBool( XML_thickBot, false );

    if (aModel.mfHeight > 0 && getFilter().isMSODocument())
    {
        aModel.mfHeight -= fmod(aModel.mfHeight, 0.75);  //round down to 0.75pt
    }

    // decode the column spans (space-separated list of colon-separated integer pairs)
    OUString aColSpansText = rAttribs.getString( XML_spans, OUString() );
    sal_Int32 nIndex = 0;
    while( nIndex >= 0 )
    {
        std::u16string_view aColSpanToken = o3tl::getToken(aColSpansText, 0, ' ', nIndex );
        size_t nSepPos = aColSpanToken.find( ':' );
        if (nSepPos == std::u16string_view::npos)
            continue;
        if (nSepPos > 0 && (nSepPos + 1 < aColSpanToken.size()))
        {
            // OOXML uses 1-based integer column indexes, row model expects 0-based colspans
            const sal_Int32 nCol1 = o3tl::toInt32(aColSpanToken.substr( 0, nSepPos )) - 1;
            const bool bValid1 = mrAddressConv.checkCol( nCol1, true);
            if (bValid1)
            {
                const sal_Int32 nCol2 = o3tl::toInt32(aColSpanToken.substr( nSepPos + 1 )) - 1;
                mrAddressConv.checkCol( nCol2, true);
            }
        }
    }

    // set row properties in the current sheet
    setRowModel( aModel );
}

bool SheetDataContext::importCell( const AttributeList& rAttribs )
{
    bool bValid = false;
    std::string_view p = rAttribs.getView(XML_r);

    if (!p.empty())
    {
        bValid = mrAddressConv.convertToCellAddress(maCellData.maCellAddr, OUString::fromUtf8(p),
                                                    mnSheet, true);
        if (bValid)
            mnCol = maCellData.maCellAddr.Col();
    }

    if (!bValid)
    {
        ++mnCol;
        ScAddress aAddress( mnCol, mnRow, mnSheet );
        bValid = mrAddressConv.checkCellAddress( aAddress, true );
        maCellData.maCellAddr = aAddress;
    }

    if( bValid )
    {
        maCellData.mnCellType     = rAttribs.getToken( XML_t, XML_n );
        maCellData.mnXfId         = rAttribs.getInteger( XML_s, -1 );
        maCellData.mbShowPhonetic = rAttribs.getBool( XML_ph, false );
        // cm attributre marks a dynamic array master cell that points at a
        // cellMetadata block in xl/metadata.xml. The actual metadata index
        // is irrelevant for us currently - we only need to know it's a dynamic
        //  array master.
        mbCellMetadata = rAttribs.getInteger(XML_cm, 0) > 0;

        // reset cell value, formula settings, and inline string
        maCellValue.clear();
        mxInlineStr.reset();
        mbHasFormula = false;

        // update used area of the sheet
        extendUsedArea( maCellData.maCellAddr );
    }
    return bValid;
}

void SheetDataContext::importFormula( const AttributeList& rAttribs )
{
    mbHasFormula = true;
    mbValidRange = mrAddressConv.convertToCellRange( maFmlaData.maFormulaRef, rAttribs.getString( XML_ref, OUString() ), mnSheet, true, true );

    maFmlaData.mnFormulaType = rAttribs.getToken( XML_t, XML_normal );
    maFmlaData.mnSharedId    = rAttribs.getInteger( XML_si, -1 );
    mbCalcAlways = rAttribs.getBool( XML_ca, false );

    if( maFmlaData.mnFormulaType == XML_dataTable )
    {
        maTableData.maRef1        = rAttribs.getString( XML_r1, OUString() );
        maTableData.maRef2        = rAttribs.getString( XML_r2, OUString() );
        maTableData.mb2dTable     = rAttribs.getBool( XML_dt2D, false );
        maTableData.mbRowTable    = rAttribs.getBool( XML_dtr, false );
        maTableData.mbRef1Deleted = rAttribs.getBool( XML_del1, false );
        maTableData.mbRef2Deleted = rAttribs.getBool( XML_del2, false );
    }

    maFormulaStr.clear();
}

void SheetDataContext::importRow( SequenceInputStream& rStrm )
{
    RowModel aModel;
    sal_Int32 nSpanCount;
    sal_uInt16 nHeight, nFlags1;
    sal_uInt8 nFlags2;
    maCurrPos.mnRow = rStrm.readInt32();
    aModel.mnXfId = rStrm.readInt32();
    nHeight = rStrm.readuInt16();
    nFlags1 = rStrm.readuInt16();
    nFlags2 = rStrm.readuChar();
    nSpanCount = rStrm.readInt32();
    maCurrPos.mnCol = 0;

    mrAddressConv.checkRow( maCurrPos.mnRow, true);
    // row index is 0-based in BIFF12, but RowModel expects 1-based
    aModel.mnRow          = maCurrPos.mnRow + 1;
    // row height is in twips in BIFF12, convert to points
    aModel.mfHeight       = nHeight / 20.0;
    aModel.mnLevel        = extractValue< sal_Int32 >( nFlags1, 8, 3 );
    aModel.mbCustomHeight = getFlag( nFlags1, BIFF12_ROW_CUSTOMHEIGHT );
    aModel.mbCustomFormat = getFlag( nFlags1, BIFF12_ROW_CUSTOMFORMAT );
    aModel.mbShowPhonetic = getFlag( nFlags2, BIFF12_ROW_SHOWPHONETIC );
    aModel.mbHidden       = getFlag( nFlags1, BIFF12_ROW_HIDDEN );
    aModel.mbCollapsed    = getFlag( nFlags1, BIFF12_ROW_COLLAPSED );
    aModel.mbThickTop     = getFlag( nFlags1, BIFF12_ROW_THICKTOP );
    aModel.mbThickBottom  = getFlag( nFlags1, BIFF12_ROW_THICKBOTTOM );

    // read the column spans
    for( sal_Int32 nSpanIdx = 0; (nSpanIdx < nSpanCount) && !rStrm.isEof(); ++nSpanIdx )
    {
        sal_Int32 nFirstCol, nLastCol;
        nFirstCol = rStrm.readInt32();
        mrAddressConv.checkCol( nFirstCol, true);
        nLastCol = rStrm.readInt32();
        mrAddressConv.checkCol( nLastCol, true);
    }

    // set row properties in the current sheet
    setRowModel( aModel );
}

bool SheetDataContext::readCellHeader( SequenceInputStream& rStrm, CellType eCellType )
{
    switch( eCellType )
    {
        case CELLTYPE_VALUE:
        case CELLTYPE_FORMULA:  maCurrPos.mnCol = rStrm.readInt32();   break;
        case CELLTYPE_MULTI:    ++maCurrPos.mnCol;          break;
    }

    sal_uInt32 nXfId = rStrm.readuInt32();

    bool bValidAddr = mrAddressConv.convertToCellAddress( maCellData.maCellAddr, maCurrPos, mnSheet, true );
    maCellData.mnXfId = extractValue< sal_Int32 >( nXfId, 0, 24 );
    maCellData.mbShowPhonetic = getFlag( nXfId, BIFF12_CELL_SHOWPHONETIC );

    // update used area of the sheet
    if( bValidAddr )
        extendUsedArea( maCellData.maCellAddr );
    return bValidAddr;
}

ApiTokenSequence SheetDataContext::readCellFormula( SequenceInputStream& rStrm )
{
    rStrm.skip( 2 );
    return mxFormulaParser->importFormula( maCellData.maCellAddr, FormulaType::Cell, rStrm );
}

bool SheetDataContext::readFormulaRef( SequenceInputStream& rStrm )
{
    BinRange aRange;
    rStrm >> aRange;
    return mrAddressConv.convertToCellRange( maFmlaData.maFormulaRef, aRange, mnSheet, true, true );
}

void SheetDataContext::importCellBool( SequenceInputStream& rStrm, CellType eCellType )
{
    if( readCellHeader( rStrm, eCellType ) )
    {
        maCellData.mnCellType = XML_b;
        bool bValue = rStrm.readuInt8() != 0;
        if( eCellType == CELLTYPE_FORMULA )
            mrSheetData.setFormulaCell( maCellData, readCellFormula( rStrm ) );
        else
            mrSheetData.setBooleanCell( maCellData, bValue );
    }
}

void SheetDataContext::importCellBlank( SequenceInputStream& rStrm, CellType eCellType )
{
    OSL_ENSURE( eCellType != CELLTYPE_FORMULA, "SheetDataContext::importCellBlank - no formula cells supported" );
    if( readCellHeader( rStrm, eCellType ) )
        mrSheetData.setBlankCell( maCellData );
}

void SheetDataContext::importCellDouble( SequenceInputStream& rStrm, CellType eCellType )
{
    if( readCellHeader( rStrm, eCellType ) )
    {
        maCellData.mnCellType = XML_n;
        double fValue = rStrm.readDouble();
        if( eCellType == CELLTYPE_FORMULA )
            mrSheetData.setFormulaCell( maCellData, readCellFormula( rStrm ) );
        else
            mrSheetData.setValueCell( maCellData, fValue );
    }
}

void SheetDataContext::importCellError( SequenceInputStream& rStrm, CellType eCellType )
{
    if( readCellHeader( rStrm, eCellType ) )
    {
        maCellData.mnCellType = XML_e;
        sal_uInt8 nErrorCode = rStrm.readuInt8();
        if( eCellType == CELLTYPE_FORMULA )
            mrSheetData.setFormulaCell( maCellData, readCellFormula( rStrm ) );
        else
            mrSheetData.setErrorCell( maCellData, nErrorCode );
    }
}

void SheetDataContext::importCellRk( SequenceInputStream& rStrm, CellType eCellType )
{
    OSL_ENSURE( eCellType != CELLTYPE_FORMULA, "SheetDataContext::importCellRk - no formula cells supported" );
    if( readCellHeader( rStrm, eCellType ) )
    {
        maCellData.mnCellType = XML_n;
        mrSheetData.setValueCell( maCellData, BiffHelper::calcDoubleFromRk( rStrm.readInt32() ) );
    }
}

void SheetDataContext::importCellRString( SequenceInputStream& rStrm, CellType eCellType )
{
    OSL_ENSURE( eCellType != CELLTYPE_FORMULA, "SheetDataContext::importCellRString - no formula cells supported" );
    if( readCellHeader( rStrm, eCellType ) )
    {
        maCellData.mnCellType = XML_inlineStr;
        RichStringRef xString = std::make_shared<RichString>();
        xString->importString( rStrm, true, *this );
        xString->finalizeImport( *this );
        mrSheetData.setStringCell( maCellData, xString );
    }
}

void SheetDataContext::importCellSi( SequenceInputStream& rStrm, CellType eCellType )
{
    OSL_ENSURE( eCellType != CELLTYPE_FORMULA, "SheetDataContext::importCellSi - no formula cells supported" );
    if( readCellHeader( rStrm, eCellType ) )
    {
        maCellData.mnCellType = XML_s;
        mrSheetData.setStringCell( maCellData, rStrm.readInt32() );
    }
}

void SheetDataContext::importCellString( SequenceInputStream& rStrm, CellType eCellType )
{
    if( readCellHeader( rStrm, eCellType ) )
    {
        maCellData.mnCellType = XML_inlineStr;
        // always import the string, stream will point to formula afterwards, if existing
        RichStringRef xString = std::make_shared<RichString>();
        xString->importString( rStrm, false, *this );
        xString->finalizeImport( *this );
        if( eCellType == CELLTYPE_FORMULA )
            mrSheetData.setFormulaCell( maCellData, readCellFormula( rStrm ) );
        else
            mrSheetData.setStringCell( maCellData, xString );
    }
}

void SheetDataContext::importArray( SequenceInputStream& rStrm )
{
    if( readFormulaRef( rStrm ) && maFmlaData.isValidArrayRef( maCellData.maCellAddr ) )
    {
        rStrm.skip( 1 );
        ApiTokenSequence aTokens = mxFormulaParser->importFormula( maCellData.maCellAddr, FormulaType::Array, rStrm );
        mrSheetData.createArrayFormula( maFmlaData.maFormulaRef, aTokens );
    }
}

void SheetDataContext::importDataTable( SequenceInputStream& rStrm )
{
    if( !readFormulaRef( rStrm ) )
        return;

    BinAddress aRef1, aRef2;
    sal_uInt8 nFlags;
    rStrm >> aRef1 >> aRef2;
    nFlags = rStrm.readuChar();
    maTableData.maRef1        = FormulaProcessorBase::generateAddress2dString( aRef1, false );
    maTableData.maRef2        = FormulaProcessorBase::generateAddress2dString( aRef2, false );
    maTableData.mbRowTable    = getFlag( nFlags, BIFF12_DATATABLE_ROW );
    maTableData.mb2dTable     = getFlag( nFlags, BIFF12_DATATABLE_2D );
    maTableData.mbRef1Deleted = getFlag( nFlags, BIFF12_DATATABLE_REF1DEL );
    maTableData.mbRef2Deleted = getFlag( nFlags, BIFF12_DATATABLE_REF2DEL );
    mrSheetData.createTableOperation( maFmlaData.maFormulaRef, maTableData );
}

void SheetDataContext::importSharedFmla( SequenceInputStream& rStrm )
{
    if( readFormulaRef( rStrm ) && maFmlaData.isValidSharedRef( maCellData.maCellAddr ) )
    {
        ApiTokenSequence aTokens = mxFormulaParser->importFormula( maCellData.maCellAddr, FormulaType::SharedFormula, rStrm );
        mrSheetData.createSharedFormula( maCellData.maCellAddr, aTokens );
    }
}

} // namespace oox

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
