/* -*- 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 <TokenWriter.hxx>
#include <comphelper/diagnose_ex.hxx>
#include <tools/stream.hxx>
#include <osl/diagnose.h>
#include <rtl/tencinfo.h>
#include <sal/log.hxx>
#include <i18nlangtag/languagetag.hxx>
#include <RtfReader.hxx>
#include <HtmlReader.hxx>
#include <strings.hxx>
#include <comphelper/types.hxx>
#include <connectivity/dbtools.hxx>
#include <com/sun/star/sdb/CommandType.hpp>
#include <com/sun/star/sdb/DatabaseContext.hpp>
#include <com/sun/star/sdbc/XConnection.hpp>
#include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
#include <com/sun/star/sdbc/XResultSetMetaDataSupplier.hpp>
#include <com/sun/star/sdbc/XRowSet.hpp>
#include <com/sun/star/sdbcx/XTablesSupplier.hpp>
#include <com/sun/star/sdb/XQueriesSupplier.hpp>
#include <com/sun/star/awt/FontWeight.hpp>
#include <com/sun/star/awt/FontStrikeout.hpp>
#include <com/sun/star/awt/FontSlant.hpp>
#include <com/sun/star/awt/FontUnderline.hpp>
#include <com/sun/star/document/DocumentProperties.hpp>
#include <svtools/htmlkywd.hxx>
#include <svtools/rtfkeywd.hxx>
#include <tools/color.hxx>
#include <svtools/htmlout.hxx>
#include <sfx2/frmhtmlw.hxx>
#include <svl/numuno.hxx>
#include <utility>
#include <vcl/svapp.hxx>
#include <UITools.hxx>
#include <toolkit/helper/vclunohelper.hxx>
#include <vcl/outdev.hxx>
#include <vcl/settings.hxx>
#include <svtools/rtfout.hxx>
#include <svtools/htmlcfg.hxx>
#include <o3tl/string_view.hxx>
#include <connectivity/formattedcolumnvalue.hxx>
#include <memory>
 
using namespace dbaui;
using namespace dbtools;
using namespace svx;
using namespace ::com::sun::star;
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::beans;
using namespace ::com::sun::star::container;
using namespace ::com::sun::star::sdbc;
using namespace ::com::sun::star::sdb;
using namespace ::com::sun::star::lang;
using namespace ::com::sun::star::sdbcx;
using namespace ::com::sun::star::awt;
using namespace ::com::sun::star::util;
 
#define CELL_X                      1437
 
ODatabaseImportExport::ODatabaseImportExport(const svx::ODataAccessDescriptor& _aDataDescriptor,
                                             const Reference< XComponentContext >& _rM,
                                             const Reference< css::util::XNumberFormatter >& _rxNumberF)
    :m_bBookmarkSelection( false )
    ,m_pStream(nullptr)
    ,m_xFormatter(_rxNumberF)
    ,m_xContext(_rM)
    ,m_nCommandType(CommandType::TABLE)
    ,m_bNeedToReInitialize(false)
    ,m_bInInitialize(false)
    ,m_bCheckOnly(false)
{
    m_eDestEnc = osl_getThreadTextEncoding();
 
    osl_atomic_increment( &m_refCount );
    impl_initFromDescriptor( _aDataDescriptor, false );
    osl_atomic_decrement( &m_refCount );
}
 
// import data
ODatabaseImportExport::ODatabaseImportExport( ::dbtools::SharedConnection _xConnection,
        const Reference< XNumberFormatter >& _rxNumberF, const Reference< XComponentContext >& _rM )
    :m_bBookmarkSelection( false )
    ,m_pStream(nullptr)
    ,m_xConnection(std::move(_xConnection))
    ,m_xFormatter(_rxNumberF)
    ,m_xContext(_rM)
    ,m_nCommandType(css::sdb::CommandType::TABLE)
    ,m_bNeedToReInitialize(false)
    ,m_bInInitialize(false)
    ,m_bCheckOnly(false)
{
    m_eDestEnc = osl_getThreadTextEncoding();
}
 
ODatabaseImportExport::~ODatabaseImportExport()
{
    acquire();
    dispose();
}
 
void ODatabaseImportExport::dispose()
{
    // remove me as listener
    Reference< XComponent >  xComponent(m_xConnection, UNO_QUERY);
    if (xComponent.is())
    {
        Reference< XEventListener> xEvt(this);
        xComponent->removeEventListener(xEvt);
    }
    m_xConnection.clear();
 
    ::comphelper::disposeComponent(m_xRow);
 
    m_xObject.clear();
    m_xResultSetMetaData.clear();
    m_xResultSet.clear();
    m_xRow.clear();
    m_xRowLocate.clear();
    m_xFormatter.clear();
    m_xRowSetColumns.clear();
}
 
void SAL_CALL ODatabaseImportExport::disposing( const EventObject& Source )
{
    Reference<XConnection> xCon(Source.Source,UNO_QUERY);
    if(m_xConnection.is() && m_xConnection == xCon)
    {
        m_xConnection.clear();
        dispose();
        m_bNeedToReInitialize = true;
    }
}
 
void ODatabaseImportExport::initialize( const ODataAccessDescriptor& _aDataDescriptor )
{
    impl_initFromDescriptor( _aDataDescriptor, true );
}
 
void ODatabaseImportExport::impl_initFromDescriptor( const ODataAccessDescriptor& _aDataDescriptor, bool _bPlusDefaultInit)
{
    if ( !_bPlusDefaultInit )
    {
        m_sDataSourceName = _aDataDescriptor.getDataSource();
        _aDataDescriptor[DataAccessDescriptorProperty::CommandType] >>= m_nCommandType;
        _aDataDescriptor[DataAccessDescriptorProperty::Command]     >>= m_sName;
        // some additional information
        if(_aDataDescriptor.has(DataAccessDescriptorProperty::Connection))
        {
            Reference< XConnection > xPureConn( _aDataDescriptor[DataAccessDescriptorProperty::Connection], UNO_QUERY );
            m_xConnection.reset( xPureConn, SharedConnection::NoTakeOwnership );
            Reference< XEventListener> xEvt(this);
            Reference< XComponent >  xComponent(m_xConnection, UNO_QUERY);
            if (xComponent.is() && xEvt.is())
                xComponent->addEventListener(xEvt);
        }
 
        if ( _aDataDescriptor.has( DataAccessDescriptorProperty::Selection ) )
            _aDataDescriptor[ DataAccessDescriptorProperty::Selection ] >>= m_aSelection;
 
        if ( _aDataDescriptor.has( DataAccessDescriptorProperty::BookmarkSelection ) )
            _aDataDescriptor[ DataAccessDescriptorProperty::BookmarkSelection ] >>= m_bBookmarkSelection;
 
        if ( _aDataDescriptor.has( DataAccessDescriptorProperty::Cursor ) )
        {
            _aDataDescriptor[ DataAccessDescriptorProperty::Cursor ] >>= m_xResultSet;
            m_xRowLocate.set( m_xResultSet, UNO_QUERY );
        }
 
        if ( m_aSelection.hasElements() )
        {
            if ( !m_xResultSet.is() )
            {
                SAL_WARN("dbaccess.ui", "ODatabaseImportExport::impl_initFromDescriptor: selection without result set is nonsense!" );
                m_aSelection.realloc( 0 );
            }
        }
 
        if ( m_aSelection.hasElements() )
        {
            if ( m_bBookmarkSelection && !m_xRowLocate.is() )
            {
                SAL_WARN("dbaccess.ui", "ODatabaseImportExport::impl_initFromDescriptor: no XRowLocate -> no bookmarks!" );
                m_aSelection.realloc( 0 );
            }
        }
    }
    else
        initialize();
}
 
void ODatabaseImportExport::initialize()
{
    m_bInInitialize = true;
    m_bNeedToReInitialize = false;
 
    if ( !m_xConnection.is() )
    {   // we need a connection
        OSL_ENSURE(!m_sDataSourceName.isEmpty(),"There must be a datsource name!");
        Reference<XNameAccess> xDatabaseContext( DatabaseContext::create(m_xContext), UNO_QUERY_THROW);
        Reference< XEventListener> xEvt(this);
 
        Reference< XConnection > xConnection;
        SQLExceptionInfo aInfo = ::dbaui::createConnection( m_sDataSourceName, xDatabaseContext, m_xContext, xEvt, xConnection );
        m_xConnection.reset( xConnection );
 
        if(aInfo.isValid() && aInfo.getType() == SQLExceptionInfo::TYPE::SQLException)
            throw *static_cast<const SQLException*>(aInfo);
    }
 
    Reference<XNameAccess> xNameAccess;
    switch(m_nCommandType)
    {
        case CommandType::TABLE:
            {
                // only for tables
                Reference<XTablesSupplier> xSup(m_xConnection,UNO_QUERY);
                if(xSup.is())
                    xNameAccess = xSup->getTables();
            }
            break;
        case CommandType::QUERY:
            {
                Reference<XQueriesSupplier> xSup(m_xConnection,UNO_QUERY);
                if(xSup.is())
                    xNameAccess = xSup->getQueries();
            }
            break;
    }
    if(xNameAccess.is() && xNameAccess->hasByName(m_sName))
    {
        xNameAccess->getByName(m_sName) >>= m_xObject;
    }
 
    if(m_xObject.is())
    {
        try
        {
            if(m_xObject->getPropertySetInfo()->hasPropertyByName(PROPERTY_FONT))
                m_xObject->getPropertyValue(PROPERTY_FONT) >>= m_aFont;
 
            // the result set may be already set with the datadescriptor
            if ( !m_xResultSet.is() )
            {
                m_xResultSet.set( m_xContext->getServiceManager()->createInstanceWithContext(u"com.sun.star.sdb.RowSet"_ustr, m_xContext), UNO_QUERY );
                Reference< XPropertySet > xProp( m_xResultSet, UNO_QUERY_THROW );
                xProp->setPropertyValue( PROPERTY_ACTIVE_CONNECTION, Any( m_xConnection.getTyped() ) );
                xProp->setPropertyValue( PROPERTY_COMMAND_TYPE, Any( m_nCommandType ) );
                xProp->setPropertyValue( PROPERTY_COMMAND, Any( m_sName ) );
                Reference< XRowSet > xRowSet( xProp, UNO_QUERY );
                xRowSet->execute();
            }
            if ( !m_xRow.is() && m_xResultSet.is() )
            {
                m_xRow.set( m_xResultSet, UNO_QUERY );
                m_xRowLocate.set( m_xResultSet, UNO_QUERY );
                m_xResultSetMetaData = Reference<XResultSetMetaDataSupplier>(m_xRow,UNO_QUERY_THROW)->getMetaData();
                Reference<XColumnsSupplier> xSup(m_xResultSet,UNO_QUERY_THROW);
                m_xRowSetColumns.set(xSup->getColumns(),UNO_QUERY_THROW);
            }
        }
        catch(Exception& )
        {
            m_xRow = nullptr;
            m_xResultSetMetaData = nullptr;
            ::comphelper::disposeComponent(m_xResultSet);
            throw;
        }
    }
    if ( m_aFont.Name.isEmpty() )
    {
        vcl::Font aApplicationFont = OutputDevice::GetDefaultFont(
            DefaultFontType::SANS_UNICODE,
            Application::GetSettings().GetUILanguageTag().getLanguageType(),
            GetDefaultFontFlags::OnlyOne
        );
        m_aFont = VCLUnoHelper::CreateFontDescriptor( aApplicationFont );
    }
 
    m_bInInitialize = false;
}
 
bool ODatabaseImportExport::Write()
{
    if ( m_bNeedToReInitialize )
    {
        if ( !m_bInInitialize )
            initialize();
    }
    return true;
}
 
bool ODatabaseImportExport::Read()
{
    if ( m_bNeedToReInitialize )
    {
        if ( !m_bInInitialize )
            initialize();
    }
    return true;
}
 
bool ORTFImportExport::Write()
{
    ODatabaseImportExport::Write();
    m_pStream->WriteChar( '{' ).WriteOString( OOO_STRING_SVTOOLS_RTF_RTF );
    m_pStream->WriteOString(OOO_STRING_SVTOOLS_RTF_ANSI);
    if (sal_uInt32 nCpg = rtl_getWindowsCodePageFromTextEncoding(m_eDestEnc); nCpg && nCpg != 65001)
    {
        m_pStream->WriteOString(OOO_STRING_SVTOOLS_RTF_ANSICPG).WriteNumberAsString(nCpg);
    }
    m_pStream->WriteOString(SAL_NEWLINE_STRING);
 
    bool bBold          = ( css::awt::FontWeight::BOLD     == m_aFont.Weight );
    bool bItalic        = ( css::awt::FontSlant_ITALIC     == m_aFont.Slant );
    bool bUnderline     = ( css::awt::FontUnderline::NONE  != m_aFont.Underline );
    bool bStrikeout     = ( css::awt::FontStrikeout::NONE  != m_aFont.Strikeout );
 
    ::Color aColor;
    if(m_xObject.is())
        m_xObject->getPropertyValue(PROPERTY_TEXTCOLOR) >>= aColor;
 
    OString aFonts(OUStringToOString(m_aFont.Name, RTL_TEXTENCODING_MS_1252));
    if (aFonts.isEmpty())
    {
        OUString aName = Application::GetSettings().GetStyleSettings().GetAppFont().GetFamilyName();
        aFonts = OUStringToOString(aName, RTL_TEXTENCODING_MS_1252);
    }
 
    m_pStream->WriteOString( "{\\fonttbl" );
    if (!aFonts.isEmpty())
    {
        sal_Int32 nIdx{0};
        sal_Int32 nTok{-1}; // to compensate pre-increment
        do {
            m_pStream->WriteOString( "\\f" );
            m_pStream->WriteNumberAsString(++nTok);
            m_pStream->WriteOString( "\\fcharset0\\fnil " );
            m_pStream->WriteOString( o3tl::getToken(aFonts, 0, ';', nIdx) );
            m_pStream->WriteChar( ';' );
        } while (nIdx>=0);
    }
    m_pStream->WriteChar( '}' ) ;
    m_pStream->WriteOString( SAL_NEWLINE_STRING );
    // write the rtf color table
    m_pStream->WriteChar( '{' ).WriteOString( OOO_STRING_SVTOOLS_RTF_COLORTBL ).WriteOString( OOO_STRING_SVTOOLS_RTF_RED );
    m_pStream->WriteNumberAsString(aColor.GetRed());
    m_pStream->WriteOString( OOO_STRING_SVTOOLS_RTF_GREEN );
    m_pStream->WriteNumberAsString(aColor.GetGreen());
    m_pStream->WriteOString( OOO_STRING_SVTOOLS_RTF_BLUE );
    m_pStream->WriteNumberAsString(aColor.GetBlue());
 
    m_pStream->WriteOString( ";\\red255\\green255\\blue255;\\red192\\green192\\blue192;}" )
                .WriteOString( SAL_NEWLINE_STRING );
 
    static char const aCell1[] = "\\clbrdrl\\brdrs\\brdrcf0\\clbrdrt\\brdrs\\brdrcf0\\clbrdrb\\brdrs\\brdrcf0\\clbrdrr\\brdrs\\brdrcf0\\clshdng10000\\clcfpat2\\cellx";
 
    m_pStream->WriteOString( OOO_STRING_SVTOOLS_RTF_TROWD ).WriteOString( OOO_STRING_SVTOOLS_RTF_TRGAPH );
    m_pStream->WriteOString("40");
    m_pStream->WriteOString( SAL_NEWLINE_STRING );
 
    if(m_xObject.is())
    {
        Reference<XColumnsSupplier> xColSup(m_xObject,UNO_QUERY);
        Reference<XNameAccess> xColumns = xColSup->getColumns();
        Sequence< OUString> aNames(xColumns->getElementNames());
 
        sal_Int32 nCount = aNames.getLength();
        bool bUseResultMetaData = false;
        if ( !nCount )
        {
            nCount = m_xResultSetMetaData->getColumnCount();
            bUseResultMetaData = true;
        }
 
        for( sal_Int32 i=1; i<=nCount; ++i )
        {
            m_pStream->WriteOString( aCell1 );
            m_pStream->WriteNumberAsString(i*CELL_X);
            m_pStream->WriteOString( SAL_NEWLINE_STRING );
        }
 
        // column description
        m_pStream->WriteChar( '{' ).WriteOString( SAL_NEWLINE_STRING );
        m_pStream->WriteOString( "\\trrh-270\\pard\\intbl" );
 
        std::unique_ptr<OString[]> pHorzChar(new OString[nCount]);
 
        for ( sal_Int32 i=1; i <= nCount; ++i )
        {
            sal_Int32 nAlign = 0;
            OUString sColumnName;
            if ( bUseResultMetaData )
                sColumnName = m_xResultSetMetaData->getColumnName(i);
            else
            {
                sColumnName = aNames[i - 1];
                Reference<XPropertySet> xColumn;
                xColumns->getByName(sColumnName) >>= xColumn;
                xColumn->getPropertyValue(PROPERTY_ALIGN) >>= nAlign;
            }
 
            const char* pChar;
            switch( nAlign )
            {
                case 1: pChar = OOO_STRING_SVTOOLS_RTF_QC;  break;
                case 2: pChar = OOO_STRING_SVTOOLS_RTF_QR;  break;
                case 0:
                default:pChar = OOO_STRING_SVTOOLS_RTF_QL;  break;
            }
 
            pHorzChar[i-1] = pChar; // to avoid to always rummage in the ITEMSET later on
 
            m_pStream->WriteOString( SAL_NEWLINE_STRING );
            m_pStream->WriteChar( '{' );
            m_pStream->WriteOString( OOO_STRING_SVTOOLS_RTF_QC );   // column header always centered
 
            if ( bBold )        m_pStream->WriteOString( OOO_STRING_SVTOOLS_RTF_B );
            if ( bItalic )      m_pStream->WriteOString( OOO_STRING_SVTOOLS_RTF_I );
            if ( bUnderline )   m_pStream->WriteOString( OOO_STRING_SVTOOLS_RTF_UL );
            if ( bStrikeout )   m_pStream->WriteOString( OOO_STRING_SVTOOLS_RTF_STRIKE );
 
            m_pStream->WriteOString( "\\fs20\\f0\\cf0\\cb2" );
            m_pStream->WriteChar( ' ' );
            RTFOutFuncs::Out_String(*m_pStream, sColumnName, m_eDestEnc);
 
            m_pStream->WriteOString( OOO_STRING_SVTOOLS_RTF_CELL );
            m_pStream->WriteChar( '}' );
            m_pStream->WriteOString( SAL_NEWLINE_STRING );
            m_pStream->WriteOString( OOO_STRING_SVTOOLS_RTF_PARD ).WriteOString( OOO_STRING_SVTOOLS_RTF_INTBL );
        }
 
        m_pStream->WriteOString( OOO_STRING_SVTOOLS_RTF_ROW );
        m_pStream->WriteOString( SAL_NEWLINE_STRING ).WriteChar( '}' );
        m_pStream->WriteOString( SAL_NEWLINE_STRING );
 
        sal_Int32 k=1;
        sal_Int32 kk=0;
        if ( m_aSelection.hasElements() )
        {
            for (auto& any : m_aSelection)
            {
                if ( m_bBookmarkSelection )
                {
                    if (!m_xRowLocate->moveToBookmark(any))
                        break;
                }
                else
                {
                    sal_Int32 nPos = -1;
                    OSL_VERIFY(any >>= nPos);
                    if (!m_xResultSet->absolute(nPos))
                        break;
                }
 
                appendRow(pHorzChar.get(), nCount, k, kk);
            }
        }
        else
        {
            m_xResultSet->beforeFirst(); // set back before the first row
            while(m_xResultSet->next())
            {
                appendRow(pHorzChar.get(),nCount,k,kk);
            }
        }
    }
 
    m_pStream->WriteChar( '}' ).WriteOString( SAL_NEWLINE_STRING );
    m_pStream->WriteUChar( 0 );
    return ((*m_pStream).GetError() == ERRCODE_NONE);
}
 
void ORTFImportExport::appendRow(OString const * pHorzChar,sal_Int32 _nColumnCount,sal_Int32& k,sal_Int32& kk)
{
    ++kk;
    m_pStream->WriteOString( OOO_STRING_SVTOOLS_RTF_TROWD ).WriteOString( OOO_STRING_SVTOOLS_RTF_TRGAPH );
    m_pStream->WriteOString("40");
    m_pStream->WriteOString( SAL_NEWLINE_STRING );
 
    static char const aCell2[] = "\\clbrdrl\\brdrs\\brdrcf2\\clbrdrt\\brdrs\\brdrcf2\\clbrdrb\\brdrs\\brdrcf2\\clbrdrr\\brdrs\\brdrcf2\\clshdng10000\\clcfpat1\\cellx";
 
    for ( sal_Int32 i=1; i<=_nColumnCount; ++i )
    {
        m_pStream->WriteOString( aCell2 );
        m_pStream->WriteNumberAsString(i*CELL_X);
        m_pStream->WriteOString( SAL_NEWLINE_STRING );
    }
 
    const bool bBold            = ( css::awt::FontWeight::BOLD     == m_aFont.Weight );
    const bool bItalic      = ( css::awt::FontSlant_ITALIC     == m_aFont.Slant );
    const bool bUnderline       = ( css::awt::FontUnderline::NONE  != m_aFont.Underline );
    const bool bStrikeout       = ( css::awt::FontStrikeout::NONE  != m_aFont.Strikeout );
    Reference< XRowSet > xRowSet(m_xRow,UNO_QUERY);
 
    m_pStream->WriteChar( '{' );
    m_pStream->WriteOString( "\\trrh-270\\pard\\intbl" );
    for ( sal_Int32 i=1; i <= _nColumnCount; ++i )
    {
        m_pStream->WriteOString( SAL_NEWLINE_STRING );
        m_pStream->WriteChar( '{' );
        m_pStream->WriteOString( pHorzChar[i-1] );
 
        if ( bBold )        m_pStream->WriteOString( OOO_STRING_SVTOOLS_RTF_B );
        if ( bItalic )      m_pStream->WriteOString( OOO_STRING_SVTOOLS_RTF_I );
        if ( bUnderline )   m_pStream->WriteOString( OOO_STRING_SVTOOLS_RTF_UL );
        if ( bStrikeout )   m_pStream->WriteOString( OOO_STRING_SVTOOLS_RTF_STRIKE );
 
        m_pStream->WriteOString( "\\fs20\\f1\\cf0\\cb1 " );
 
        try
        {
            Reference<XPropertySet> xColumn(m_xRowSetColumns->getByIndex(i-1),UNO_QUERY_THROW);
            dbtools::FormattedColumnValue aFormatedValue(m_xContext,xRowSet,xColumn);
            OUString sValue = aFormatedValue.getFormattedValue();
            if ( !sValue.isEmpty() )
                RTFOutFuncs::Out_String(*m_pStream,sValue,m_eDestEnc);
        }
        catch (Exception&)
        {
            SAL_WARN("dbaccess.ui","RTF WRITE!");
        }
 
        m_pStream->WriteOString( OOO_STRING_SVTOOLS_RTF_CELL );
        m_pStream->WriteChar( '}' );
        m_pStream->WriteOString( SAL_NEWLINE_STRING );
        m_pStream->WriteOString( OOO_STRING_SVTOOLS_RTF_PARD ).WriteOString( OOO_STRING_SVTOOLS_RTF_INTBL );
    }
    m_pStream->WriteOString( OOO_STRING_SVTOOLS_RTF_ROW ).WriteOString( SAL_NEWLINE_STRING );
    m_pStream->WriteChar( '}' );
    ++k;
}
 
bool ORTFImportExport::Read()
{
    ODatabaseImportExport::Read();
    SvParserState eState = SvParserState::Error;
    if ( m_pStream )
    {
        tools::SvRef<ORTFReader> xReader(new ORTFReader((*m_pStream),m_xConnection,m_xFormatter,m_xContext));
        if ( isCheckEnabled() )
            xReader->enableCheckOnly();
        eState = xReader->CallParser();
    }
 
    return eState != SvParserState::Error;
}
 
const sal_Int16 OHTMLImportExport::nCellSpacing = 0;
const char OHTMLImportExport::sIndentSource[nIndentMax+1] = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
 
OHTMLImportExport::OHTMLImportExport(const svx::ODataAccessDescriptor& _aDataDescriptor,
                                     const Reference< XComponentContext >& _rM,
                                     const Reference< css::util::XNumberFormatter >& _rxNumberF)
        : ODatabaseImportExport(_aDataDescriptor,_rM,_rxNumberF)
    ,m_nIndent(0)
#if OSL_DEBUG_LEVEL > 0
    ,m_bCheckFont(false)
#endif
{
    // set HTML configuration
    m_eDestEnc = RTL_TEXTENCODING_UTF8;
    strncpy( sIndent, sIndentSource ,std::min(sizeof(sIndent),sizeof(sIndentSource)));
    sIndent[0] = 0;
}
 
bool OHTMLImportExport::Write()
{
    ODatabaseImportExport::Write();
    if(m_xObject.is())
    {
        m_pStream->WriteChar( '<' ).WriteOString( OOO_STRING_SVTOOLS_HTML_doctype ).WriteChar( ' ' ).WriteOString( OOO_STRING_SVTOOLS_HTML_doctype5 ).WriteChar( '>' ).WriteOString( SAL_NEWLINE_STRING ).WriteOString( SAL_NEWLINE_STRING );
        HTMLOutFuncs::Out_AsciiTag(*m_pStream, OOO_STRING_SVTOOLS_HTML_html).WriteOString(SAL_NEWLINE_STRING).WriteOString(GetIndentStr());
        WriteHeader();
        m_pStream->WriteOString(SAL_NEWLINE_STRING).WriteOString(GetIndentStr());
        WriteBody();
        m_pStream->WriteOString(SAL_NEWLINE_STRING).WriteOString(GetIndentStr());
        HTMLOutFuncs::Out_AsciiTag(*m_pStream, OOO_STRING_SVTOOLS_HTML_html, false).WriteOString(SAL_NEWLINE_STRING).WriteOString(GetIndentStr());
 
        return ((*m_pStream).GetError() == ERRCODE_NONE);
    }
    return false;
}
 
bool OHTMLImportExport::Read()
{
    ODatabaseImportExport::Read();
    SvParserState eState = SvParserState::Error;
    if ( m_pStream )
    {
        tools::SvRef<OHTMLReader> xReader(new OHTMLReader((*m_pStream),m_xConnection,m_xFormatter,m_xContext));
        if ( isCheckEnabled() )
            xReader->enableCheckOnly();
        xReader->SetTableName(m_sDefaultTableName);
        eState = xReader->CallParser();
    }
 
    return eState != SvParserState::Error;
}
 
void OHTMLImportExport::WriteHeader()
{
    uno::Reference<document::XDocumentProperties> xDocProps(
        document::DocumentProperties::create( m_xContext ) );
    if (xDocProps.is()) {
        xDocProps->setTitle(m_sName);
    }
 
    IncIndent(1);
    HTMLOutFuncs::Out_AsciiTag(*m_pStream, OOO_STRING_SVTOOLS_HTML_head).WriteOString(SAL_NEWLINE_STRING).WriteOString(GetIndentStr());
 
    SfxFrameHTMLWriter::Out_DocInfo( (*m_pStream), OUString(),
        xDocProps, sIndent );
    m_pStream->WriteOString(SAL_NEWLINE_STRING).WriteOString(GetIndentStr());
    IncIndent(-1);
    m_pStream->WriteOString(SAL_NEWLINE_STRING).WriteOString(GetIndentStr());
    HTMLOutFuncs::Out_AsciiTag(*m_pStream, OOO_STRING_SVTOOLS_HTML_head, false).WriteOString(SAL_NEWLINE_STRING).WriteOString(GetIndentStr());
}
 
void OHTMLImportExport::WriteBody()
{
    IncIndent(1);
    m_pStream->WriteOString( "<" ).WriteOString( OOO_STRING_SVTOOLS_HTML_style ).WriteOString( " " ).WriteOString( OOO_STRING_SVTOOLS_HTML_O_type ).WriteOString( "=\"text/css\">" );
 
    m_pStream->WriteOString( "<!-- " );
    m_pStream->WriteOString(SAL_NEWLINE_STRING).WriteOString(GetIndentStr());
    m_pStream->WriteOString( OOO_STRING_SVTOOLS_HTML_body ).WriteOString( " { " ).WriteOString( "font-family: " ).WriteChar( '"' ).WriteOString( OUStringToOString(m_aFont.Name, osl_getThreadTextEncoding()) ).WriteChar( '\"' );
        // TODO : think about the encoding of the font name
    m_pStream->WriteOString( "; " ).WriteOString( "font-size: " );
    m_pStream->WriteNumberAsString(m_aFont.Height);
    m_pStream->WriteChar( '}' );
 
    m_pStream->WriteOString(SAL_NEWLINE_STRING).WriteOString(GetIndentStr());
    m_pStream->WriteOString( " -->" );
    IncIndent(-1);
    m_pStream->WriteOString(SAL_NEWLINE_STRING).WriteOString(GetIndentStr());
    HTMLOutFuncs::Out_AsciiTag(*m_pStream, OOO_STRING_SVTOOLS_HTML_style, false).WriteOString(SAL_NEWLINE_STRING).WriteOString(GetIndentStr());
    m_pStream->WriteOString(SAL_NEWLINE_STRING).WriteOString(GetIndentStr());
 
    // default Textcolour black
    m_pStream->WriteChar( '<' ).WriteOString( OOO_STRING_SVTOOLS_HTML_body ).WriteChar( ' ' ).WriteOString( OOO_STRING_SVTOOLS_HTML_O_text ).WriteChar( '=' );
    ::Color aColor;
    if(m_xObject.is())
        m_xObject->getPropertyValue(PROPERTY_TEXTCOLOR) >>= aColor;
    HTMLOutFuncs::Out_Color( (*m_pStream), aColor );
 
    m_pStream->WriteOString( " " OOO_STRING_SVTOOLS_HTML_O_bgcolor "=" );
    HTMLOutFuncs::Out_Color( (*m_pStream), aColor );
 
    m_pStream->WriteChar( '>' );
    m_pStream->WriteOString(SAL_NEWLINE_STRING).WriteOString(GetIndentStr());
 
    WriteTables();
 
    HTMLOutFuncs::Out_AsciiTag(*m_pStream, OOO_STRING_SVTOOLS_HTML_body, false).WriteOString(SAL_NEWLINE_STRING).WriteOString(GetIndentStr());
}
 
void OHTMLImportExport::WriteTables()
{
    OString aStrOut  = OOO_STRING_SVTOOLS_HTML_table
            " "
            OOO_STRING_SVTOOLS_HTML_frame
            "="
            OOO_STRING_SVTOOLS_HTML_TF_void ""_ostr;
 
    Sequence< OUString> aNames;
    Reference<XNameAccess> xColumns;
    bool bUseResultMetaData = false;
    if(m_xObject.is())
    {
        Reference<XColumnsSupplier> xColSup(m_xObject,UNO_QUERY);
        xColumns = xColSup->getColumns();
        aNames = xColumns->getElementNames();
        if ( !aNames.hasElements() )
        {
            sal_Int32 nCount = m_xResultSetMetaData->getColumnCount();
            aNames.realloc(nCount);
            auto aNamesRange = asNonConstRange(aNames);
            for (sal_Int32 i= 0; i < nCount; ++i)
                aNamesRange[i] = m_xResultSetMetaData->getColumnName(i+1);
            bUseResultMetaData = true;
        }
    }
 
    aStrOut += " "
            OOO_STRING_SVTOOLS_HTML_O_align
            "="
            OOO_STRING_SVTOOLS_HTML_AL_left
            " "
            OOO_STRING_SVTOOLS_HTML_O_cellspacing
            "=" +
            OString::number(nCellSpacing) +
            " "
            OOO_STRING_SVTOOLS_HTML_O_cols
            "=" +
            OString::number(aNames.getLength()) +
            " "
            OOO_STRING_SVTOOLS_HTML_O_border
            "=1";
 
    IncIndent(1);
    HTMLOutFuncs::Out_AsciiTag(*m_pStream, aStrOut);
 
    FontOn();
 
    HTMLOutFuncs::Out_AsciiTag(*m_pStream, OOO_STRING_SVTOOLS_HTML_caption);
    HTMLOutFuncs::Out_AsciiTag(*m_pStream, OOO_STRING_SVTOOLS_HTML_bold);
 
    m_pStream->WriteOString( OUStringToOString(m_sName, osl_getThreadTextEncoding()) );
        // TODO : think about the encoding of the name
    HTMLOutFuncs::Out_AsciiTag(*m_pStream, OOO_STRING_SVTOOLS_HTML_bold, false);
    HTMLOutFuncs::Out_AsciiTag(*m_pStream, OOO_STRING_SVTOOLS_HTML_caption, false);
 
    FontOff();
    m_pStream->WriteOString(SAL_NEWLINE_STRING).WriteOString(GetIndentStr());
    // </FONT>
 
    IncIndent(1);
    HTMLOutFuncs::Out_AsciiTag(*m_pStream, OOO_STRING_SVTOOLS_HTML_thead).WriteOString(SAL_NEWLINE_STRING).WriteOString(GetIndentStr());
 
    IncIndent(1);
    HTMLOutFuncs::Out_AsciiTag(*m_pStream, OOO_STRING_SVTOOLS_HTML_tablerow).WriteOString(SAL_NEWLINE_STRING).WriteOString(GetIndentStr());
 
    if(m_xObject.is())
    {
        std::unique_ptr<sal_Int32[]> pFormat(new sal_Int32[aNames.getLength()]);
 
        std::unique_ptr<const char *[]> pHorJustify(new const char*[aNames.getLength()]);
        std::unique_ptr<sal_Int32[]> pColWidth(new sal_Int32[aNames.getLength()]);
 
        sal_Int32 nHeight = 0;
        m_xObject->getPropertyValue(PROPERTY_ROW_HEIGHT) >>= nHeight;
 
        // 1. writing the column description
        for (sal_Int32 i = 0; i < aNames.getLength(); ++i)
        {
            sal_Int32 nAlign = 0;
            pFormat[i] = 0;
            pColWidth[i] = 100;
            if ( !bUseResultMetaData )
            {
                Reference<XPropertySet> xColumn;
                xColumns->getByName(aNames[i]) >>= xColumn;
                xColumn->getPropertyValue(PROPERTY_ALIGN) >>= nAlign;
                pFormat[i] = ::comphelper::getINT32(xColumn->getPropertyValue(PROPERTY_FORMATKEY));
                pColWidth[i] = ::comphelper::getINT32(xColumn->getPropertyValue(PROPERTY_WIDTH));
            }
 
            switch( nAlign )
            {
                case 1:     pHorJustify[i] = OOO_STRING_SVTOOLS_HTML_AL_center; break;
                case 2:     pHorJustify[i] = OOO_STRING_SVTOOLS_HTML_AL_right;  break;
                default:    pHorJustify[i] = OOO_STRING_SVTOOLS_HTML_AL_left;       break;
            }
 
            if(i == aNames.getLength()-1)
                IncIndent(-1);
 
            WriteCell(pFormat[i],pColWidth[i],nHeight,pHorJustify[i],aNames[i],OOO_STRING_SVTOOLS_HTML_tableheader);
        }
 
        IncIndent(-1);
        HTMLOutFuncs::Out_AsciiTag(*m_pStream, OOO_STRING_SVTOOLS_HTML_tablerow, false).WriteOString(SAL_NEWLINE_STRING).WriteOString(GetIndentStr());
        HTMLOutFuncs::Out_AsciiTag(*m_pStream, OOO_STRING_SVTOOLS_HTML_thead, false).WriteOString(SAL_NEWLINE_STRING).WriteOString(GetIndentStr());
 
        IncIndent(1);
        HTMLOutFuncs::Out_AsciiTag(*m_pStream, OOO_STRING_SVTOOLS_HTML_tbody).WriteOString(SAL_NEWLINE_STRING).WriteOString(GetIndentStr());
 
        // 2. and now the data
        Reference< XRowSet > xRowSet(m_xRow,UNO_QUERY);
        m_xResultSet->beforeFirst(); // set back before the first row
        while(m_xResultSet->next())
        {
            IncIndent(1);
            HTMLOutFuncs::Out_AsciiTag(*m_pStream, OOO_STRING_SVTOOLS_HTML_tablerow).WriteOString(SAL_NEWLINE_STRING).WriteOString(GetIndentStr());
 
            for(sal_Int32 i=1;i<=aNames.getLength();++i)
            {
                if(i == aNames.getLength())
                    IncIndent(-1);
 
                OUString aValue;
                try
                {
                    Reference<XPropertySet> xColumn(m_xRowSetColumns->getByIndex(i-1),UNO_QUERY_THROW);
                    dbtools::FormattedColumnValue aFormatedValue(m_xContext,xRowSet,xColumn);
                    OUString sValue = aFormatedValue.getFormattedValue();
                    if (!sValue.isEmpty())
                    {
                        aValue = sValue;
                    }
                }
                catch( const Exception& )
                {
                    DBG_UNHANDLED_EXCEPTION("dbaccess");
                }
                WriteCell(pFormat[i-1],pColWidth[i-1],nHeight,pHorJustify[i-1],aValue,OOO_STRING_SVTOOLS_HTML_tabledata);
            }
            HTMLOutFuncs::Out_AsciiTag(*m_pStream, OOO_STRING_SVTOOLS_HTML_tablerow, false).WriteOString(SAL_NEWLINE_STRING).WriteOString(GetIndentStr());
        }
    }
    else
    {
        IncIndent(-1);
        HTMLOutFuncs::Out_AsciiTag(*m_pStream, OOO_STRING_SVTOOLS_HTML_tablerow, false).WriteOString(SAL_NEWLINE_STRING).WriteOString(GetIndentStr());
        HTMLOutFuncs::Out_AsciiTag(*m_pStream, OOO_STRING_SVTOOLS_HTML_thead, false).WriteOString(SAL_NEWLINE_STRING).WriteOString(GetIndentStr());
 
        IncIndent(1);
        HTMLOutFuncs::Out_AsciiTag(*m_pStream, OOO_STRING_SVTOOLS_HTML_tbody).WriteOString(SAL_NEWLINE_STRING).WriteOString(GetIndentStr());
    }
 
    IncIndent(-1);
    m_pStream->WriteOString(SAL_NEWLINE_STRING).WriteOString(GetIndentStr());
    HTMLOutFuncs::Out_AsciiTag(*m_pStream, OOO_STRING_SVTOOLS_HTML_tbody, false).WriteOString(SAL_NEWLINE_STRING).WriteOString(GetIndentStr());
    IncIndent(-1);
    HTMLOutFuncs::Out_AsciiTag(*m_pStream, OOO_STRING_SVTOOLS_HTML_table, false).WriteOString(SAL_NEWLINE_STRING).WriteOString(GetIndentStr());
}
 
void OHTMLImportExport::WriteCell( sal_Int32 nFormat, sal_Int32 nWidthPixel, sal_Int32 nHeightPixel, const char* pChar,
                                   const OUString& rValue, const char* pHtmlTag)
{
    OString aStrTD = pHtmlTag;
 
    nWidthPixel  = nWidthPixel  ? nWidthPixel   : 86;
    nHeightPixel = nHeightPixel ? nHeightPixel  : 17;
 
    // despite the <TABLE COLS=n> and <COL WIDTH=x> designation necessary,
    // as Netscape is not paying attention to them.
    // column width
    aStrTD += " "
            OOO_STRING_SVTOOLS_HTML_O_width
            "=" +
            OString::number(nWidthPixel) +
    // line height
            " "
            OOO_STRING_SVTOOLS_HTML_O_height
            "=" +
            OString::number(nHeightPixel) +
            " "
            OOO_STRING_SVTOOLS_HTML_O_align
            "=" +
            pChar;
 
    SvNumberFormatsSupplierObj* pSupplierImpl = m_xFormatter.is() ? comphelper::getFromUnoTunnel<SvNumberFormatsSupplierObj>(m_xFormatter->getNumberFormatsSupplier()) : nullptr;
    SvNumberFormatter* pFormatter = pSupplierImpl ? pSupplierImpl->GetNumberFormatter() : nullptr;
    if(pFormatter)
    {
        double fVal = 0.0;
 
        try
        {
            fVal = m_xFormatter->convertStringToNumber(nFormat,rValue);
            HTMLOutFuncs::CreateTableDataOptionsValNum(false, fVal,nFormat, *pFormatter);
        }
        catch(const Exception&)
        {
            HTMLOutFuncs::CreateTableDataOptionsValNum(false, fVal,nFormat, *pFormatter);
        }
    }
 
    HTMLOutFuncs::Out_AsciiTag(*m_pStream, aStrTD);
 
    FontOn();
 
    bool bBold          = ( css::awt::FontWeight::BOLD     == m_aFont.Weight );
    bool bItalic        = ( css::awt::FontSlant_ITALIC     == m_aFont.Slant );
    bool bUnderline     = ( css::awt::FontUnderline::NONE  != m_aFont.Underline );
    bool bStrikeout     = ( css::awt::FontStrikeout::NONE  != m_aFont.Strikeout );
 
    if ( bBold )        HTMLOutFuncs::Out_AsciiTag(*m_pStream, OOO_STRING_SVTOOLS_HTML_bold);
    if ( bItalic )      HTMLOutFuncs::Out_AsciiTag(*m_pStream, OOO_STRING_SVTOOLS_HTML_italic);
    if ( bUnderline )   HTMLOutFuncs::Out_AsciiTag(*m_pStream, OOO_STRING_SVTOOLS_HTML_underline);
    if ( bStrikeout )   HTMLOutFuncs::Out_AsciiTag(*m_pStream, OOO_STRING_SVTOOLS_HTML_strike);
 
    if ( rValue.isEmpty() )
        HTMLOutFuncs::Out_AsciiTag(*m_pStream, OOO_STRING_SVTOOLS_HTML_linebreak);        // no completely empty cell
    else
        HTMLOutFuncs::Out_String( (*m_pStream), rValue );
 
    if ( bStrikeout )   HTMLOutFuncs::Out_AsciiTag(*m_pStream, OOO_STRING_SVTOOLS_HTML_strike, false);
    if ( bUnderline )   HTMLOutFuncs::Out_AsciiTag(*m_pStream, OOO_STRING_SVTOOLS_HTML_underline, false);
    if ( bItalic )      HTMLOutFuncs::Out_AsciiTag(*m_pStream, OOO_STRING_SVTOOLS_HTML_italic, false);
    if ( bBold )        HTMLOutFuncs::Out_AsciiTag(*m_pStream, OOO_STRING_SVTOOLS_HTML_bold, false);
 
    FontOff();
 
    HTMLOutFuncs::Out_AsciiTag(*m_pStream, pHtmlTag, false).WriteOString(SAL_NEWLINE_STRING).WriteOString(GetIndentStr());
}
 
void OHTMLImportExport::FontOn()
{
#if OSL_DEBUG_LEVEL > 0
    m_bCheckFont = true;
#endif
 
    // <FONT FACE="xxx">
    OString aStrOut  = "<"
            OOO_STRING_SVTOOLS_HTML_font
            " "
            OOO_STRING_SVTOOLS_HTML_O_face
            "="
            "\"" +
            OUStringToOString(m_aFont.Name,osl_getThreadTextEncoding()) +
        // TODO : think about the encoding of the font name
            "\""
            " "
            OOO_STRING_SVTOOLS_HTML_O_color
            "=";
    m_pStream->WriteOString( aStrOut );
 
    ::Color aColor;
    if(m_xObject.is())
        m_xObject->getPropertyValue(PROPERTY_TEXTCOLOR) >>= aColor;
 
    HTMLOutFuncs::Out_Color( (*m_pStream), aColor );
    m_pStream->WriteOString( ">" );
}
 
inline void OHTMLImportExport::FontOff()
{
#if OSL_DEBUG_LEVEL > 0
    OSL_ENSURE(m_bCheckFont,"No FontOn() called");
#endif
    HTMLOutFuncs::Out_AsciiTag(*m_pStream, OOO_STRING_SVTOOLS_HTML_font, false);
#if OSL_DEBUG_LEVEL > 0
    m_bCheckFont = false;
#endif
}
 
void OHTMLImportExport::IncIndent( sal_Int16 nVal )
{
    sIndent[m_nIndent] = '\t';
    m_nIndent = m_nIndent + nVal;
    if ( m_nIndent < 0 )
        m_nIndent = 0;
    else if ( m_nIndent > nIndentMax )
        m_nIndent = nIndentMax;
    sIndent[m_nIndent] = 0;
}
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

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

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

V1053 Calling the 'initialize' virtual function indirectly in the constructor may lead to unexpected result at runtime. Check lines: 'TokenWriter.cxx:94', 'TokenWriter.cxx:207', 'TokenWriter.hxx:92'.

V1053 Calling the 'acquire' virtual function in the destructor may lead to unexpected result at runtime.

V524 It is odd that the body of 'Read' function is fully equivalent to the body of 'Write' function.

V785 Constant expression in switch statement.

V785 Constant expression in switch statement.