/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*************************************************************************
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (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.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: IBM Corporation
 *
 *  Copyright: 2008 by IBM Corporation
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/
/**
 * @file
 *  For LWP filter architecture prototype - table cell numerics format
 */
 
#include <lwpoverride.hxx>
#include "lwptblcell.hxx"
#include "lwppara.hxx"
#include "lwptblformula.hxx"
 
#include "lwptablelayout.hxx"
#include <osl/thread.h>
#include <rtl/ustrbuf.hxx>
#include <sal/log.hxx>
#include <memory>
 
LwpFormulaArg::~LwpFormulaArg()
{
}
 
 LwpFormulaInfo::LwpFormulaInfo(LwpObjectHeader const &objHdr, LwpSvStream* pStrm)
    : LwpCellList(objHdr, pStrm)
    , m_bSupported(true)
    , m_nFormulaRow(0)
{}
 
LwpFormulaInfo::~LwpFormulaInfo()
{
    while(m_aStack.size()>0)
    {
        LwpFormulaArg* pArg=m_aStack.back();
        m_aStack.pop_back();
        delete pArg; pArg=nullptr;
    }
}
 
void LwpFormulaInfo::ReadConst()
{
    double Constant = m_pObjStrm->QuickReadDouble();
 
    m_aStack.push_back( new LwpFormulaConst(Constant) );
}
 
/**
*   Need more effort for unicode.
*/
void LwpFormulaInfo::ReadText()
{
    m_pObjStrm->QuickReadInt16(); //Disk Size
    sal_uInt16 nStrLen = m_pObjStrm->QuickReadInt16();
 
    std::unique_ptr<char[]> pBuf(new char[nStrLen+1]);
    m_pObjStrm->QuickRead( pBuf.get(), nStrLen );
    *(pBuf.get()+nStrLen)='\0';
    OUString aText;
    aText += "\"";
    aText += OUString(pBuf.get(), nStrLen, osl_getThreadTextEncoding());
    aText += "\"";
 
    m_aStack.push_back(new LwpFormulaText(aText));
}
 
void LwpFormulaInfo::ReadCellID()
{
    LwpRowSpecifier RowSpecifier;
    LwpColumnSpecifier ColumnSpecifier;
 
    RowSpecifier.QuickRead(m_pObjStrm.get());
    ColumnSpecifier.QuickRead(m_pObjStrm.get());
 
    m_aStack.push_back( new LwpFormulaCellAddr(ColumnSpecifier.ColumnID(cColumn),
                                                RowSpecifier.RowID(m_nFormulaRow)) );
}
 
void LwpFormulaInfo::ReadCellRange()
{
    ReadCellID( ); // start
    LwpFormulaCellAddr* pStartCellAddr = static_cast<LwpFormulaCellAddr*>(m_aStack.back());
    m_aStack.pop_back();
 
    ReadCellID(); // end
    LwpFormulaCellAddr* pEndCellAddr = static_cast<LwpFormulaCellAddr*>(m_aStack.back());
    m_aStack.pop_back();
 
    m_aStack.push_back( new LwpFormulaCellRangeAddr(pStartCellAddr->GetCol(),
                                                    pStartCellAddr->GetRow(),
                                                    pEndCellAddr->GetCol(),
                                                    pEndCellAddr->GetRow()) );
    delete pStartCellAddr;
    delete pEndCellAddr;
}
 
/**
*   Read expression from wordpro file
*/
void LwpFormulaInfo::ReadExpression()
{
    sal_uInt16 TokenType, DiskLength;
 
    /* Read the compiled expression length */
    m_pObjStrm->SeekRel(2);
 
    bool bError = false;
    while ((TokenType = m_pObjStrm->QuickReaduInt16(&bError)) != TK_END)
    {
 
        if (bError)
            throw std::runtime_error("error reading expression");
 
        // Get the disk length of this token
        DiskLength = m_pObjStrm->QuickReaduInt16();
 
        switch (TokenType)
        {
            case TK_CONSTANT:
            {
                ReadConst();
                break;
            }
 
            case TK_CELLID:
                ReadCellID();
                break;
 
            case TK_CELLRANGE:
                ReadCellRange();
                break;
 
            case TK_SUM:
            case TK_IF:
            case TK_COUNT:
            case TK_MINIMUM:
            case TK_MAXIMUM:
            case TK_AVERAGE:
                {
                    std::unique_ptr<LwpFormulaFunc> xFunc(new LwpFormulaFunc(TokenType));
                    ReadArguments(*xFunc);
                    m_aStack.push_back(xFunc.release());
                }
                break;
 
            case TK_ADD://7
            case TK_SUBTRACT:
            case TK_MULTIPLY:
            case TK_DIVIDE:
            case TK_LESS:
            case TK_LESS_OR_EQUAL:
            case TK_GREATER:
            case TK_GREATER_OR_EQUAL:
            case TK_EQUAL:
            case TK_NOT_EQUAL:
            case TK_AND:
            case TK_OR:
            case TK_NOT:
                m_pObjStrm->SeekRel(DiskLength); // extensible for future
 
                if (m_aStack.size() >= 2)
                {//binary operator
                    LwpFormulaOp* pOp = new LwpFormulaOp(TokenType);
                    pOp->AddArg(m_aStack.back()); m_aStack.pop_back();
                    pOp->AddArg(m_aStack.back()); m_aStack.pop_back();
                    m_aStack.push_back(pOp);
                }
                break;
            case TK_UNARY_MINUS:
                if (!m_aStack.empty())
                {
                    LwpFormulaUnaryOp* pOp = new LwpFormulaUnaryOp(TokenType);
                    pOp->AddArg(m_aStack.back()); m_aStack.pop_back();
                    m_aStack.push_back(pOp);
                }
                break;
            default:
                // We don't know what to do with this token, so eat it.
                m_pObjStrm->SeekRel(DiskLength);
                break;
        }
        MarkUnsupported(TokenType);
    }
}
 
void LwpFormulaInfo::MarkUnsupported(sal_uInt16 TokenType)
{
    switch(TokenType)
    {
    case TK_IF:
    case TK_COUNT:
    case TK_NOT:
        {
            m_bSupported = false;//Not supported formulas
        }
        break;
    default:
        break;
    }
}
/**
*   Read arguments of functions from wordpro file
*   @param  LwpFormulaFunc& aFunc, functions object
*/
void LwpFormulaInfo::ReadArguments(LwpFormulaFunc& aFunc)
{
    sal_uInt16 NumberOfArguments = m_pObjStrm->QuickReaduInt16();
 
    for (sal_uInt16 Count = 0; Count < NumberOfArguments; Count++)
    {
        sal_uInt8 ArgumentType = static_cast<sal_uInt8>(m_pObjStrm->QuickReaduInt16()); // written as lushort
        sal_uInt16 ArgumentDiskLength = m_pObjStrm->QuickReaduInt16();
        bool bArgument = true;
 
        switch(ArgumentType)
        {
            case TK_CELLID:
                ReadCellID();
                break;
 
            case TK_CELLRANGE:
                ReadCellRange();
                break;
 
            case TK_CONSTANT:
                ReadConst();
                break;
 
            case TK_TEXT:
                ReadText();
                break;
 
            case TK_EXPRESSION:
                ReadExpression();
                break;
 
            default:
                bArgument = false;
                m_pObjStrm->SeekRel(ArgumentDiskLength);
                break;
        }
 
        if (bArgument && !m_aStack.empty())
        {
            aFunc.AddArg(m_aStack.back());
            m_aStack.pop_back();
        }
    }
}
 
void LwpFormulaInfo::Read()
{
    LwpCellList::Read();
    {
        LwpRowList* pRowList = dynamic_cast<LwpRowList*>(cParent.obj().get());
        if (pRowList)
        {
            m_nFormulaRow = pRowList->GetRowID();
        }
        else
        {
            SAL_WARN("lwp", "missing row list");
        }
    }
    m_pObjStrm->SeekRel(2);//flags, size in file: sal_uInt16
 
    LwpNotifyListPersistent cNotifyList;
    cNotifyList.Read(m_pObjStrm.get());
 
    ReadExpression();
 
    m_pObjStrm->SkipExtra();
}
 
/**
*   Make the formula string.
*/
OUString  LwpFormulaInfo::Convert(LwpTableLayout* pCellsMap)
{
    OUString aFormula;
    if (m_bSupported)
    {
        if(1==m_aStack.size())
        {
            LwpFormulaArg* pFormula = m_aStack.back();
            aFormula = pFormula->ToString(pCellsMap);
        }
        else
        {
            assert(false);
        }
    }
    return aFormula;
}
 
/**
*   Fill the XFCell content
*/
void LwpFormulaInfo::Convert(XFCell * pCell,LwpTableLayout* pCellsMap)
{
    OUString aFormula;
    aFormula = Convert(pCellsMap);
    if (!aFormula.isEmpty())
    {
        pCell->SetFormula(aFormula);
    }
    LwpCellList::Convert(pCell);
}
 
LwpFormulaConst::LwpFormulaConst(double dVal)
{
    m_dVal = dVal;
}
 
OUString LwpFormulaConst::ToString(LwpTableLayout* /*pCellsMap*/)
{
    return OUString::number(m_dVal);
}
 
LwpFormulaText::LwpFormulaText( const OUString& aText)
{
    m_aText = aText;
}
 
LwpFormulaCellAddr::LwpFormulaCellAddr(sal_Int16 aCol, sal_Int16 aRow)
{
    m_aCol = aCol;
    m_aRow = aRow;
}
 
OUString LwpFormulaCellAddr::ToString(LwpTableLayout* pCellsMap)
{
    OUString aCellAddr = "<" + LwpFormulaTools::GetCellAddr(m_aRow,m_aCol,pCellsMap) + ">";
    return aCellAddr;
}
 
LwpFormulaCellRangeAddr::LwpFormulaCellRangeAddr(sal_Int16 aStartCol,
                                                 sal_Int16 aStartRow,
                                                 sal_Int16 aEndCol,
                                                 sal_Int16 aEndRow)
{
    m_aStartCol = aStartCol;
    m_aStartRow = aStartRow;
    m_aEndCol = aEndCol;
    m_aEndRow = aEndRow;
}
 
/**
*   Convert the cell range into a string
*/
OUString LwpFormulaCellRangeAddr::ToString(LwpTableLayout* pCellsMap)
{
    OUString aCellAddr = "<"
        + LwpFormulaTools::GetCellAddr(m_aStartRow,m_aStartCol,pCellsMap) + ":"
        + LwpFormulaTools::GetCellAddr(m_aEndRow,m_aEndCol,pCellsMap) + ">";
 
    return aCellAddr;
}
 
LwpFormulaFunc::LwpFormulaFunc(sal_uInt16 nTokenType)
{
    m_nTokenType = nTokenType;
}
 
LwpFormulaFunc::~LwpFormulaFunc()
{
    while(m_aArgs.size()>0)
    {
        LwpFormulaArg* pArg = m_aArgs.back();
        m_aArgs.pop_back();
        delete pArg;pArg=nullptr;
    }
}
void LwpFormulaFunc::AddArg(LwpFormulaArg* pArg)
{
    m_aArgs.push_back(pArg);
}
/**
*   Convert the functions to a string, which is a argument of other formula
*/
OUString LwpFormulaFunc::ToArgString(LwpTableLayout* pCellsMap)
{
    OUStringBuffer aFormula;
    aFormula.append('(');
    aFormula.append(ToString(pCellsMap));
    aFormula.append(')');
    return aFormula.makeStringAndClear();
}
/**
*   Convert the function to a formula string.
*/
OUString LwpFormulaFunc::ToString(LwpTableLayout* pCellsMap)
{
    OUStringBuffer aFormula;
 
    OUString aFuncName = LwpFormulaTools::GetName(m_nTokenType);
    aFormula.append(aFuncName);
    aFormula.append(" ");//Append a blank space
 
    //Append args
    for (auto const& elem : m_aArgs)
    {
        aFormula.append(elem->ToArgString(pCellsMap)).append("|"); //separator
    }
 
    //erase the last "|"
    if (!m_aArgs.empty())
    {
        aFormula.setLength(aFormula.getLength()-1);
    }
    else
    {
        assert(false);
    }
 
    return aFormula.makeStringAndClear();
}
 
/**
*   Convert the formula in operators to a string : e.g. 1+2+3
*/
OUString LwpFormulaOp::ToString(LwpTableLayout* pCellsMap)
{
    OUString aFormula;
    if (2==m_aArgs.size())
    {
        std::vector<LwpFormulaArg*>::iterator aItr = m_aArgs.end();
        --aItr;
 
        aFormula += (*aItr)->ToArgString(pCellsMap) + " ";
        OUString aFuncName = LwpFormulaTools::GetName(m_nTokenType);
 
        aFormula += aFuncName + " ";
 
        --aItr;
        aFormula += (*aItr)->ToArgString(pCellsMap);
    }
    else
    {
        assert(false);
    }
    return aFormula;
}
 
/**
*   convert the formula in unary operators into string : e.g. -2
*/
OUString LwpFormulaUnaryOp::ToString(LwpTableLayout* pCellsMap)
{
    OUString aFormula;
    if (1==m_aArgs.size())
    {
        OUString aFuncName = LwpFormulaTools::GetName(m_nTokenType);
        aFormula += aFuncName;
 
        std::vector<LwpFormulaArg*>::iterator aItr = m_aArgs.begin();
        aFormula += (*aItr)->ToArgString(pCellsMap);
    }
    else
    {
        assert(false);
    }
    return aFormula;
}
/**
*   Get token name
*/
OUString LwpFormulaTools::GetName(sal_uInt16 nTokenType)
{
    OUString aName;
    switch(nTokenType)
    {
    case TK_SUM:
        aName = "SUM";
        break;
    case TK_IF:
        aName = "IF";//Not supported by SODC
        break;
    case TK_COUNT:
        aName = "COUNT";//Not supported by SODC
        break;
    case TK_MINIMUM:
        aName = "MIN";
        break;
    case TK_MAXIMUM:
        aName = "MAX";
        break;
    case TK_AVERAGE:
        aName = "MEAN";
        break;
    case TK_ADD:
        aName = "+";
        break;
    case TK_SUBTRACT:
        aName = "-";
        break;
    case TK_MULTIPLY:
        aName = "*";
        break;
    case TK_DIVIDE:
        aName = "/";
        break;
    case TK_UNARY_MINUS:
        aName = "-";
        break;
    case TK_LESS:
        aName = "L";
        break;
    case TK_LESS_OR_EQUAL:
        aName = "LEQ";
        break;
    case TK_GREATER:
        aName = "G";
        break;
    case TK_GREATER_OR_EQUAL:
        aName = "GEQ";
        break;
    case TK_EQUAL:
        aName = "EQ";
        break;
    case TK_NOT_EQUAL:
        aName = "NEQ";
        break;
    case TK_NOT:
        aName = "NOT";
        break;
    case TK_AND:
        aName = "AND";
        break;
    case TK_OR:
        aName = "OR";
        break;
    default:
        assert(false);
        break;
    }
    return aName;
}
 
/**
*   Get cell address in String
*/
OUString LwpFormulaTools::GetCellAddr(sal_Int16 nRow, sal_Int16 nCol, LwpTableLayout* pCellsMap)
{
    OUString aCellAddr;
    XFCell* pCell = pCellsMap->GetCellsMap(nRow,static_cast<sal_uInt8>(nCol));
    if (pCell)
    {
        aCellAddr = pCell->GetCellName();
    }
    else
    {
        assert( -1==nRow || -1==static_cast<sal_Int8>(nCol));
    }
    return aCellAddr;
}
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

V527 It is odd that the '\0' value is assigned to 'char' type pointer. Probably meant: ** (pBuf.get() + nStrLen) = '\0'.

V783 Dereferencing of the invalid iterator 'aItr' might take place.