Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  compiler.cxx   Sprache: C

 
/* -*- 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 <config_features.h>

#include <compiler.hxx>

#include <mutex>
#include <vcl/svapp.hxx>
#include <vcl/settings.hxx>
#include <sfx2/app.hxx>
#include <sfx2/objsh.hxx>
#include <basic/sbmeth.hxx>
#include <basic/sbstar.hxx>
#include <svl/numformat.hxx>
#include <svl/zforlist.hxx>
#include <svl/sharedstringpool.hxx>
#include <sal/log.hxx>
#include <o3tl/safeint.hxx>
#include <o3tl/string_view.hxx>
#include <osl/diagnose.h>
#include <rtl/character.hxx>
#include <unotools/charclass.hxx>
#include <unotools/configmgr.hxx>
#include <com/sun/star/lang/Locale.hpp>
#include <com/sun/star/sheet/FormulaOpCodeMapEntry.hpp>
#include <com/sun/star/sheet/FormulaLanguage.hpp>
#include <com/sun/star/i18n/KParseTokens.hpp>
#include <com/sun/star/i18n/KParseType.hpp>
#include <comphelper/processfactory.hxx>
#include <comphelper/string.hxx>
#include <unotools/transliterationwrapper.hxx>
#include <tools/urlobj.hxx>
#include <rtl/math.hxx>
#include <rtl/ustring.hxx>
#include <stdlib.h>
#include <rangenam.hxx>
#include <dbdata.hxx>
#include <document.hxx>
#include <docsh.hxx>
#include <callform.hxx>
#include <addincol.hxx>
#include <refupdat.hxx>
#include <globstr.hrc>
#include <scresid.hxx>
#include <formulacell.hxx>
#include <dociter.hxx>
#include <docoptio.hxx>
#include <formula/errorcodes.hxx>
#include <parclass.hxx>
#include <autonamecache.hxx>
#include <externalrefmgr.hxx>
#include <rangeutl.hxx>
#include <convuno.hxx>
#include <tokenuno.hxx>
#include <formulaparserpool.hxx>
#include <tokenarray.hxx>
#include <scmatrix.hxx>
#include <tokenstringcontext.hxx>
#include <officecfg/Office/Common.hxx>
#include <sfx2/linkmgr.hxx>
#include <interpre.hxx>

using namespace formula;
using namespace ::com::sun::star;
using ::std::vector;

const CharClass*                    ScCompiler::pCharClassEnglish = nullptr;
const CharClass*                    ScCompiler::pCharClassLocalized = nullptr;
const ScCompiler::Convention*       ScCompiler::pConventions[ ]   = { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr };

namespace {

enum ScanState
{
    ssGetChar,
    ssGetBool,
    ssGetValue,
    ssGetString,
    ssSkipString,
    ssGetIdent,
    ssGetReference,
    ssSkipReference,
    ssGetErrorConstant,
    ssGetTableRefItem,
    ssGetTableRefColumn,
    ssStop
};

constexpr std::array<ScCharFlags, 128> makeCommonCharTable()
{
    std::array<ScCharFlags, 128> a;
    a.fill(ScCharFlags::Illegal);

    // Allow tabs/newlines.
    // Allow saving whitespace as is (as per OpenFormula specification v.1.2, clause 5.14 "Whitespace").
    a['\t'] = ScCharFlags::CharDontCare | ScCharFlags::WordSep | ScCharFlags::ValueSep;
    a['\n'] = ScCharFlags::CharDontCare | ScCharFlags::WordSep | ScCharFlags::ValueSep;
    a['\r'] = ScCharFlags::CharDontCare | ScCharFlags::WordSep | ScCharFlags::ValueSep;

    a[' '] = ScCharFlags::CharDontCare | ScCharFlags::WordSep | ScCharFlags::ValueSep;
    a['!'] = ScCharFlags::Char | ScCharFlags::WordSep | ScCharFlags::ValueSep;
    a['"'] = ScCharFlags::CharString | ScCharFlags::StringSep;
    a['#'] = ScCharFlags::WordSep | ScCharFlags::CharErrConst;
    a['$'] = ScCharFlags::CharWord | ScCharFlags::Word | ScCharFlags::CharIdent | ScCharFlags::Ident;
    a['%'] = ScCharFlags::Char | ScCharFlags::WordSep | ScCharFlags::ValueSep;
    a['&'] = ScCharFlags::Char | ScCharFlags::WordSep | ScCharFlags::ValueSep;
    a['\''] = ScCharFlags::NameSep;
    a['('] = ScCharFlags::Char | ScCharFlags::WordSep | ScCharFlags::ValueSep;
    a[')'] = ScCharFlags::Char | ScCharFlags::WordSep | ScCharFlags::ValueSep;
    a['*'] = ScCharFlags::Char | ScCharFlags::WordSep | ScCharFlags::ValueSep;
    a['+'] = ScCharFlags::Char | ScCharFlags::WordSep | ScCharFlags::ValueExp | ScCharFlags::ValueSign;
    a[','] = ScCharFlags::CharValue | ScCharFlags::Value;
    a['-'] = ScCharFlags::Char | ScCharFlags::WordSep | ScCharFlags::ValueExp | ScCharFlags::ValueSign;
    a['.'] = ScCharFlags::Word | ScCharFlags::CharValue | ScCharFlags::Value | ScCharFlags::Ident | ScCharFlags::Name;
    a['/'] = ScCharFlags::Char | ScCharFlags::WordSep | ScCharFlags::ValueSep;

    for (int i = '0'; i <= '9'; i++)
        a[i] = ScCharFlags::CharValue | ScCharFlags::Word | ScCharFlags::Value | ScCharFlags::ValueExp | ScCharFlags::ValueValue | ScCharFlags::Ident | ScCharFlags::Name;

    a[':'] = ScCharFlags::Char | ScCharFlags::Word;
    a[';'] = ScCharFlags::Char | ScCharFlags::WordSep | ScCharFlags::ValueSep;
    a['<'] = ScCharFlags::CharBool | ScCharFlags::WordSep | ScCharFlags::ValueSep;
    a['='] = ScCharFlags::Char | ScCharFlags::Bool | ScCharFlags::WordSep | ScCharFlags::ValueSep;
    a['>'] = ScCharFlags::CharBool | ScCharFlags::Bool | ScCharFlags::WordSep | ScCharFlags::ValueSep;
    a['?'] = ScCharFlags::CharWord | ScCharFlags::Word | ScCharFlags::Name;
    /* @ */ // FREE

    for (int i = 'A'; i <= 'Z'; i++)
        a[i] = ScCharFlags::CharWord | ScCharFlags::Word | ScCharFlags::CharIdent | ScCharFlags::Ident | ScCharFlags::CharName | ScCharFlags::Name;

    /* [ */ // FREE
    /* \ */ // FREE
    /* ] */ // FREE

    a['^'] = ScCharFlags::Char | ScCharFlags::WordSep | ScCharFlags::ValueSep;
    a['_'] = ScCharFlags::CharWord | ScCharFlags::Word | ScCharFlags::CharIdent | ScCharFlags::Ident | ScCharFlags::CharName | ScCharFlags::Name;
    /* ` */ // FREE

    for (int i = 'a'; i <= 'z'; i++)
        a[i] = ScCharFlags::CharWord | ScCharFlags::Word | ScCharFlags::CharIdent | ScCharFlags::Ident | ScCharFlags::CharName | ScCharFlags::Name;

    a['{'] = ScCharFlags::Char | ScCharFlags::WordSep | ScCharFlags::ValueSep; // array open
    a['|'] = ScCharFlags::Char | ScCharFlags::WordSep | ScCharFlags::ValueSep; // array row sep (Should be OOo specific)
    a['}'] = ScCharFlags::Char | ScCharFlags::WordSep | ScCharFlags::ValueSep; // array close
    a['~'] = ScCharFlags::Char;        // OOo specific
    /* 127 */ // FREE

    return a;
}

constexpr std::array<ScCharFlags, 128> makeCharTable_OOO_A1()
{
    auto a = makeCommonCharTable();
    a['['] = ScCharFlags::Char;
    a[']'] = ScCharFlags::Char;
    return a;
}

constexpr std::array<ScCharFlags, 128> makeCharTable_OOO_A1_ODF()
{
    auto a = makeCommonCharTable();
    a['!'] |= ScCharFlags::OdfLabelOp;
    a['$'] |= ScCharFlags::OdfNameMarker;
    a['['] = ScCharFlags::OdfLBracket;
    a[']'] = ScCharFlags::OdfRBracket;
    return a;
}

constexpr std::array<ScCharFlags, 128> makeCharTable_XL()
{
    auto a = makeCommonCharTable();
    a[' '] |= ScCharFlags::Word;
    a['!'] |= ScCharFlags::Ident | ScCharFlags::Word;
    a['"'] |= ScCharFlags::Word;
    a['#'] &= ~ScCharFlags::WordSep;
    a['#'] |= ScCharFlags::Word;
    a['%'] |= ScCharFlags::Word;
    a['&'] |= ScCharFlags::Word;
    a['\''] |= ScCharFlags::Word;
    a['('] |= ScCharFlags::Word;
    a[')'] |= ScCharFlags::Word;
    a['*'] |= ScCharFlags::Word;
    a['+'] |= ScCharFlags::Word;
#if 0 /* this really needs to be locale specific. */
    a[','] = ScCharFlags::Char | ScCharFlags::WordSep | ScCharFlags::ValueSep;
#else
    a[','] |= ScCharFlags::Word;
#endif
    a['-'] |= ScCharFlags::Word;

    a[';'] |= ScCharFlags::Word;
    a['<'] |= ScCharFlags::Word;
    a['='] |= ScCharFlags::Word;
    a['>'] |= ScCharFlags::Word;
    /* ? */ // question really is not permitted in sheet name
    a['@'] |= ScCharFlags::Word;
    a['['] = ScCharFlags::Word;
    a[']'] = ScCharFlags::Word;
    a['{'] |= ScCharFlags::Word;
    a['|'] |= ScCharFlags::Word;
    a['}'] |= ScCharFlags::Word;
    a['~'] |= ScCharFlags::Word;
    return a;
}

constexpr std::array<ScCharFlags, 128> makeCharTable_XL_A1()
{
    auto a = makeCharTable_XL();
    a['['] |= ScCharFlags::Char;
    a[']'] |= ScCharFlags::Char;
    return a;
}

constexpr std::array<ScCharFlags, 128> makeCharTable_XL_OOX()
{
    auto a = makeCharTable_XL_A1();
    a['['] |= ScCharFlags::CharIdent;
    a[']'] |= ScCharFlags::Ident;
    return a;
}

constexpr std::array<ScCharFlags, 128> makeCharTable_XL_R1C1()
{
    auto a = makeCharTable_XL();
    a['['] |= ScCharFlags::Ident;
    a[']'] |= ScCharFlags::Ident;
    return a;
}

const std::array<ScCharFlags, 128>& getCharTable(FormulaGrammar::AddressConvention eConv)
{
    switch (eConv)
    {
        case FormulaGrammar::CONV_OOO:
        {
            static constexpr auto table_OOO_A1(makeCharTable_OOO_A1());
            return table_OOO_A1;
        }
        case FormulaGrammar::CONV_ODF:
        {
            static constexpr auto table_OOO_A1_ODF(makeCharTable_OOO_A1_ODF());
            return table_OOO_A1_ODF;
        }
        case FormulaGrammar::CONV_XL_A1:
        {
            static constexpr auto table_XL_A1(makeCharTable_XL_A1());
            return table_XL_A1;
        }
        case FormulaGrammar::CONV_XL_R1C1:
        {
            static constexpr auto table_XL_R1C1(makeCharTable_XL_R1C1());
            return table_XL_R1C1;
        }
        case FormulaGrammar::CONV_XL_OOX:
        {
            static constexpr auto table_XL_OOX(makeCharTable_XL_OOX());
            return table_XL_OOX;
        }
        case FormulaGrammar::CONV_UNSPECIFIED:
        default:
        {
            assert(!"Unimplemented convention");
            std::abort();
        }
    }
}

}

using namespace ::com::sun::star::i18n;

void ScCompiler::fillFromAddInMap( const NonConstOpCodeMapPtr& xMap,FormulaGrammar::Grammar _eGrammar  ) const
{
    size_t nSymbolOffset;
    switch( _eGrammar )
    {
        // XFunctionAccess and XCell::setFormula()/getFormula() API always used
        // PODF grammar symbols, keep it.
        case FormulaGrammar::GRAM_API:
        case FormulaGrammar::GRAM_PODF:
            nSymbolOffset = offsetof( AddInMap, pUpper);
            break;
        default:
        case FormulaGrammar::GRAM_ODFF:
            nSymbolOffset = offsetof( AddInMap, pODFF);
            break;
        case FormulaGrammar::GRAM_ENGLISH:
            nSymbolOffset = offsetof( AddInMap, pEnglish);
            break;
    }
    const AddInMap* const pStop = g_aAddInMap + GetAddInMapCount();
    for (const AddInMap* pMap = g_aAddInMap; pMap < pStop; ++pMap)
    {
        char const * const * ppSymbol =
            reinterpret_castchar const * const * >(
                    reinterpret_castchar const * >(pMap) + nSymbolOffset);
        xMap->putExternal( OUString::createFromAscii( *ppSymbol),
                OUString::createFromAscii( pMap->pOriginal));
    }
    if (_eGrammar == FormulaGrammar::GRAM_API)
    {
        // Add English names additionally to programmatic names, so they
        // can be used in XCell::setFormula() non-localized API calls.
        // Note the reverse map will still deliver programmatic names for
        // XCell::getFormula().
        nSymbolOffset = offsetof( AddInMap, pEnglish);
        for (const AddInMap* pMap = g_aAddInMap; pMap < pStop; ++pMap)
        {
            char const * const * ppSymbol =
                reinterpret_castchar const * const * >(
                        reinterpret_castchar const * >(pMap) + nSymbolOffset);
            xMap->putExternal( OUString::createFromAscii( *ppSymbol),
                    OUString::createFromAscii( pMap->pOriginal));
        }
    }
}

void ScCompiler::fillFromAddInCollectionUpperName( const NonConstOpCodeMapPtr& xMap ) const
{
    ScUnoAddInCollection* pColl = ScGlobal::GetAddInCollection();
    tools::Long nCount = pColl->GetFuncCount();
    for (tools::Long i=0; i < nCount; ++i)
    {
        const ScUnoAddInFuncData* pFuncData = pColl->GetFuncData(i);
        if (pFuncData)
            xMap->putExternalSoftly( pFuncData->GetUpperName(),
                    pFuncData->GetOriginalName());
    }
}

void ScCompiler::fillFromAddInCollectionEnglishName( const NonConstOpCodeMapPtr& ;xMap ) const
{
    ScUnoAddInCollection* pColl = ScGlobal::GetAddInCollection();
    tools::Long nCount = pColl->GetFuncCount();
    for (tools::Long i=0; i < nCount; ++i)
    {
        const ScUnoAddInFuncData* pFuncData = pColl->GetFuncData(i);
        if (pFuncData)
        {
            const OUString aName( pFuncData->GetUpperEnglish());
            if (!aName.isEmpty())
                xMap->putExternalSoftly( aName, pFuncData->GetOriginalName());
            else
                xMap->putExternalSoftly( pFuncData->GetUpperName(),
                        pFuncData->GetOriginalName());
        }
    }
}

void ScCompiler::fillFromAddInCollectionExcelName( const NonConstOpCodeMapPtr& xMap ) const
{
    const LanguageTag aDestLang(LANGUAGE_ENGLISH_US);
    ScUnoAddInCollection* pColl = ScGlobal::GetAddInCollection();
    tools::Long nCount = pColl->GetFuncCount();
    for (tools::Long i=0; i < nCount; ++i)
    {
        OUString aExcelName;
        const ScUnoAddInFuncData* pFuncData = pColl->GetFuncData(i);
        if (pFuncData && pFuncData->GetExcelName( aDestLang, aExcelName, true))
        {
            // Note that function names not defined in OOXML but implemented by
            // Excel should have the "_xlfn." prefix. We have no way to check
            // though what an Add-In actually implements.
            xMap->putExternalSoftly( GetCharClassEnglish()->uppercase(aExcelName), pFuncData->GetOriginalName());
        }
    }
}

static std::mutex gCharClassMutex;

void ScCompiler::DeInit()
{
    std::scoped_lock aGuard(gCharClassMutex);
    if (pCharClassEnglish)
    {
        delete pCharClassEnglish;
        pCharClassEnglish = nullptr;
    }
    if (pCharClassLocalized)
    {
        delete pCharClassLocalized;
        pCharClassLocalized = nullptr;
    }
}

bool ScCompiler::IsEnglishSymbol( const OUString& rName )
{
    // function names are always case-insensitive
    OUString aUpper = GetCharClassEnglish()->uppercase(rName);

    // 1. built-in function name
    formula::FormulaCompiler aCompiler;
    OpCode eOp = aCompiler.GetEnglishOpCode( aUpper );
    if ( eOp != ocNone )
    {
        return true;
    }
    // 2. old add in functions
    if (ScGlobal::GetLegacyFuncCollection()->findByName(aUpper))
    {
        return true;
    }

    // 3. new (uno) add in functions
    OUString aIntName = ScGlobal::GetAddInCollection()->FindFunction(aUpper, false);
    return !aIntName.isEmpty();       // no valid function name
}

const CharClass* ScCompiler::GetCharClassEnglish()
{
    std::scoped_lock aGuard(gCharClassMutex);
    if (!pCharClassEnglish)
    {
        pCharClassEnglish = new CharClass( ::comphelper::getProcessComponentContext(),
                LanguageTag( LANGUAGE_ENGLISH_US));
    }
    return pCharClassEnglish;
}

const CharClass* ScCompiler::GetCharClassLocalized()
{
    // Switching UI language requires restart; if not, we would have to
    // keep track of that.
    std::scoped_lock aGuard(gCharClassMutex);
    if (!pCharClassLocalized)
    {
        pCharClassLocalized = new CharClass( ::comphelper::getProcessComponentContext(),
                Application::GetSettings().GetUILanguageTag());
    }
    return pCharClassLocalized;
}

void ScCompiler::SetGrammar( const FormulaGrammar::Grammar eGrammar )
{
    assert( eGrammar != FormulaGrammar::GRAM_UNSPECIFIED && "ScCompiler::SetGrammar: don't pass FormulaGrammar::GRAM_UNSPECIFIED");
    if (eGrammar == GetGrammar())
        return;     // nothing to be done

    if( eGrammar == FormulaGrammar::GRAM_EXTERNAL )
    {
        meGrammar = eGrammar;
        mxSymbols = GetFinalOpCodeMap( css::sheet::FormulaLanguage::NATIVE);
    }
    else
    {
        FormulaGrammar::Grammar eMyGrammar = eGrammar;
        const sal_Int32 nFormulaLanguage = FormulaGrammar::extractFormulaLanguage( eMyGrammar);
        OpCodeMapPtr xMap = GetFinalOpCodeMap( nFormulaLanguage);
        OSL_ENSURE( xMap, "ScCompiler::SetGrammar: unknown formula language");
        if (!xMap)
        {
            xMap = GetFinalOpCodeMap( css::sheet::FormulaLanguage::NATIVE);
            eMyGrammar = xMap->getGrammar();
        }

        // Save old grammar for call to SetGrammarAndRefConvention().
        FormulaGrammar::Grammar eOldGrammar = GetGrammar();
        // This also sets the grammar associated with the map!
        SetFormulaLanguage( xMap);

        // Override if necessary.
        if (eMyGrammar != GetGrammar())
            SetGrammarAndRefConvention( eMyGrammar, eOldGrammar);
    }
}

// Unclear how this was intended to be refreshed when the
// grammar or sheet count is changed ? Ideally this would be
// a method on Document that would globally cache these.
std::vector<OUString> &ScCompiler::GetSetupTabNames() const
{
    std::vector<OUString> &rTabNames = const_cast<ScCompiler *>(this)->maTabNames;

    if (rTabNames.empty())
    {
        rTabNames = rDoc.GetAllTableNames();
        for (auto& rTabName : rTabNames)
            ScCompiler::CheckTabQuotes(rTabName, formula::FormulaGrammar::extractRefConvention(meGrammar));
    }

    return rTabNames;
}

void ScCompiler::SetFormulaLanguage( const ScCompiler::OpCodeMapPtr & xMap )
{
    if (!xMap)
        return;

    mxSymbols = xMap;
    if (mxSymbols->isEnglish())
        pCharClass = GetCharClassEnglish();
    else
        pCharClass = GetCharClassLocalized();

    // The difference is needed for an uppercase() call that usually does not
    // result in different strings but for a few languages like Turkish;
    // though even de-DE and de-CH may differ in ß/SS handling..
    // At least don't care if both are English.
    // The current locale is more likely to not be "en" so check first.
    const LanguageTag& rLT1 = ScGlobal::getCharClass().getLanguageTag();
    const LanguageTag& rLT2 = pCharClass->getLanguageTag();
    mbCharClassesDiffer = (rLT1 != rLT2 && (rLT1.getLanguage() != "en" || rLT2.getLanguage() != "en"));

    SetGrammarAndRefConvention( mxSymbols->getGrammar(), GetGrammar());
}

void ScCompiler::SetGrammarAndRefConvention(
        const FormulaGrammar::Grammar eNewGrammar, const FormulaGrammar::Grammar eOldGrammar )
{
    meGrammar = eNewGrammar;    // SetRefConvention needs the new grammar set!
    FormulaGrammar::AddressConvention eConv = FormulaGrammar::extractRefConvention( meGrammar);
    if (eConv == FormulaGrammar::CONV_UNSPECIFIED && eOldGrammar == FormulaGrammar::GRAM_UNSPECIFIED)
        SetRefConvention( rDoc.GetAddressConvention());
    else
        SetRefConvention( eConv );
}

OUString ScCompiler::FindAddInFunction( const OUString& rUpperName, bool bLocalFirst ) const
{
    return ScGlobal::GetAddInCollection()->FindFunction(rUpperName, bLocalFirst);    // bLocalFirst=false for english
}

ScCompiler::Convention::~Convention()
{
}

ScCompiler::Convention::Convention( FormulaGrammar::AddressConvention eConv )
    : meConv(eConv)
    , mrCharTable(getCharTable(eConv))
{
    ScCompiler::pConventions[ meConv ] = this;
}

static bool lcl_isValidQuotedText( std::u16string_view rFormula, size_t nSrcPos, ParseResult& rRes )
{
    // Tokens that start at ' can have anything in them until a final '
    // but '' marks an escaped '
    // We've earlier guaranteed that a string containing '' will be
    // surrounded by '
    if (nSrcPos < rFormula.size() && rFormula[nSrcPos] == '\'')
    {
        size_t nPos = nSrcPos+1;
        while (nPos < rFormula.size())
        {
            if (rFormula[nPos] == '\'')
            {
                if ( (nPos+1 == rFormula.size()) || (rFormula[nPos+1] != '\'') )
                {
                    rRes.TokenType = KParseType::SINGLE_QUOTE_NAME;
                    rRes.EndPos = nPos+1;
                    return true;
                }
                ++nPos;
            }
            ++nPos;
        }
    }

    return false;
}

static bool lcl_parseExternalName(
        const OUString& rSymbol,
        OUString& rFile,
        OUString& rName,
        const sal_Unicode cSep,
        const ScDocument& rDoc,
        const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks )
{
    /* TODO: future versions will have to support sheet-local names too, thus
     * return a possible sheet name as well. */

    const sal_Unicode* const pStart = rSymbol.getStr();
    const sal_Unicode* p = pStart;
    sal_Int32 nLen = rSymbol.getLength();
    OUString aTmpFile;
    OUStringBuffer aTmpName;
    sal_Int32 i = 0;
    bool bInName = false;
    if (cSep == '!')
    {
        // For XL use existing parser that resolves bracketed and quoted and
        // indexed external document names.
        ScRange aRange;
        OUString aStartTabName, aEndTabName;
        ScRefFlags nFlags = ScRefFlags::ZERO;
        p = aRange.Parse_XL_Header( p, rDoc, aTmpFile, aStartTabName,
                aEndTabName, nFlags, true, pExternalLinks );
        if (!p || p == pStart)
            return false;
        i = sal_Int32(p - pStart);
    }
    for ( ; i < nLen; ++i, ++p)
    {
        sal_Unicode c = *p;
        if (i == 0)
        {
            if (c == '.' || c == cSep)
                return false;

            if (c == '\'')
            {
                // Move to the next char and loop until the second single
                // quote.
                sal_Unicode cPrev = c;
                ++i; ++p;
                for (sal_Int32 j = i; j < nLen; ++j, ++p)
                {
                    c = *p;
                    if (c == '\'')
                    {
                        if (j == i)
                        {
                            // empty quote e.g. (=''!Name)
                            return false;
                        }

                        if (cPrev == '\'')
                        {
                            // two consecutive quotes equal a single quote in
                            // the file name.
                            aTmpFile += OUStringChar(c);
                            cPrev = 'a';
                        }
                        else
                            cPrev = c;

                        continue;
                    }

                    if (cPrev == '\'' && j != i)
                    {
                        // this is not a quote but the previous one is.  This
                        // ends the parsing of the quoted segment.  At this
                        // point, the current char must equal the separator
                        // char.

                        i = j;
                        bInName = true;
                        aTmpName.append(c); // Keep the separator as part of the name.
                        break;
                    }
                    aTmpFile += OUStringChar(c);
                    cPrev = c;
                }

                if (!bInName)
                {
                    // premature ending of the quoted segment.
                    return false;
                }

                if (c != cSep)
                {
                    // only the separator is allowed after the closing quote.
                    return false;
                }

                continue;
            }
        }

        if (bInName)
        {
            if (c == cSep)
            {
                // A second separator ?  Not a valid external name.
                return false;
            }
            aTmpName.append(c);
        }
        else
        {
            if (c == cSep)
            {
                bInName = true;
                aTmpName.append(c); // Keep the separator as part of the name.
            }
            else
            {
                do
                {
                    if (rtl::isAsciiAlphanumeric(c))
                        // allowed.
                        break;

                    if (c > 128)
                        // non-ASCII character is allowed.
                        break;

                    bool bValid = false;
                    switch (c)
                    {
                        case '_':
                        case '-':
                        case '.':
                            // these special characters are allowed.
                            bValid = true;
                            break;
                    }
                    if (bValid)
                        break;

                    return false;
                }
                while (false);
                aTmpFile += OUStringChar(c);
            }
        }
    }

    if (!bInName)
    {
        // No name found - most likely the symbol has no '!'s.
        return false;
    }

    sal_Int32 nNameLen = aTmpName.getLength();
    if (nNameLen < 2)
    {
        // Name must be at least 2-char long (separator plus name).
        return false;
    }

    if (aTmpName[0] != cSep)
    {
        // 1st char of the name must equal the separator.
        return false;
    }

    if (aTmpName[nNameLen-1] == '!')
    {
        // Check against #REF!.
        if (OUString::unacquired(aTmpName).equalsIgnoreAsciiCase("#REF!"))
            return false;
    }

    rFile = aTmpFile;
    rName = aTmpName.makeStringAndClear().copy(1); // Skip the first char as it is always the separator.
    return true;
}

static OUString lcl_makeExternalNameStr(const OUString& rFile, const OUString& rName,
        const sal_Unicode cSep, bool bODF )
{
    OUString aEscQuote(u"''"_ustr);
    OUString aFile(rFile.replaceAll("'", aEscQuote));
    OUString aName(rName);
    if (bODF)
        aName = aName.replaceAll("'", aEscQuote);
    OUStringBuffer aBuf(aFile.getLength() + aName.getLength() + 9);
    if (bODF)
        aBuf.append( '[');
    aBuf.append( "'" + aFile + "'" + OUStringChar(cSep) );
    if (bODF)
        aBuf.append( "$$'" );
    aBuf.append( aName);
    if (bODF)
        aBuf.append( "']" );
    return aBuf.makeStringAndClear();
}

static bool lcl_getLastTabName( OUString& rTabName2, const OUString& rTabName1,
                                const vector<OUString>& rTabNames, const ScRange& rRef )
{
    SCTAB nTabSpan = rRef.aEnd.Tab() - rRef.aStart.Tab();
    if (nTabSpan > 0)
    {
        size_t nCount = rTabNames.size();
        vector<OUString>::const_iterator itrBeg = rTabNames.begin(), itrEnd = rTabNames.end();
        vector<OUString>::const_iterator itr = ::std::find(itrBeg, itrEnd, rTabName1);
        if (itr == rTabNames.end())
        {
            rTabName2 = ScResId(STR_NO_REF_TABLE);
            return false;
        }

        size_t nDist = ::std::distance(itrBeg, itr);
        if (nDist + static_cast<size_t>(nTabSpan) >= nCount)
        {
            rTabName2 = ScResId(STR_NO_REF_TABLE);
            return false;
        }

        rTabName2 = rTabNames[nDist+nTabSpan];
    }
    else
        rTabName2 = rTabName1;

    return true;
}

namespace {

struct Convention_A1 : public ScCompiler::Convention
{
    explicit Convention_A1( FormulaGrammar::AddressConvention eConv ) : ScCompiler::Convention( eConv ) { }
    static void MakeColStr( const ScSheetLimits& rLimits, OUStringBuffer& rBuffer, SCCOL nCol );
    static void MakeRowStr( const ScSheetLimits& rLimits, OUStringBuffer& rBuffer, SCROW nRow );

    ParseResult parseAnyToken( const OUString& rFormula,
                               sal_Int32 nSrcPos,
                               const CharClass* pCharClass,
                               bool bGroupSeparator) const override
    {
        ParseResult aRet;
        if ( lcl_isValidQuotedText(rFormula, nSrcPos, aRet) )
            return aRet;

        constexpr sal_Int32 nStartFlags = KParseTokens::ANY_LETTER_OR_NUMBER |
            KParseTokens::ASC_UNDERSCORE | KParseTokens::ASC_DOLLAR;
        constexpr sal_Int32 nContFlags = nStartFlags | KParseTokens::ASC_DOT;
        // '?' allowed in range names because of Xcl :-/
        static constexpr OUString aAddAllowed(u"?#"_ustr);
        return pCharClass->parseAnyToken( rFormula,
                nSrcPos, nStartFlags, aAddAllowed,
                (bGroupSeparator ? nContFlags | KParseTokens::GROUP_SEPARATOR_IN_NUMBER_3 : nContFlags),
                aAddAllowed );
    }

    virtual ScCharFlags getCharTableFlags( sal_Unicode c, sal_Unicode /*cLast*/ ) const override
    {
        return mrCharTable[static_cast<sal_uInt8>(c)];
    }
};

}

void Convention_A1::MakeColStr( const ScSheetLimits& rLimits, OUStringBuffer& rBuffer, SCCOL nCol )
{
    if ( !rLimits.ValidCol(nCol) )
        rBuffer.append(ScResId(STR_NO_REF_TABLE));
    else
        ::ScColToAlpha( rBuffer, nCol);
}

void Convention_A1::MakeRowStr( const ScSheetLimits& rLimits, OUStringBuffer& rBuffer, SCROW nRow )
{
    if ( !rLimits.ValidRow(nRow) )
        rBuffer.append(ScResId(STR_NO_REF_TABLE));
    else
        rBuffer.append(sal_Int32(nRow + 1));
}

namespace {

struct ConventionOOO_A1 : public Convention_A1
{
    ConventionOOO_A1() : Convention_A1 (FormulaGrammar::CONV_OOO) { }
    explicit ConventionOOO_A1( FormulaGrammar::AddressConvention eConv ) : Convention_A1 (eConv) { }

    static void MakeTabStr( OUStringBuffer &rBuf, const std::vector<OUString>& rTabNames, SCTAB nTab )
    {
        if (o3tl::make_unsigned(nTab) >= rTabNames.size())
            rBuf.append(ScResId(STR_NO_REF_TABLE));
        else
            rBuf.append(rTabNames[nTab]);
        rBuf.append('.');
    }

    enum SingletonDisplay
    {
        SINGLETON_NONE,
        SINGLETON_COL,
        SINGLETON_ROW
    };

    static void MakeOneRefStrImpl(
        const ScSheetLimits& rLimits, OUStringBuffer& rBuffer,
        std::u16string_view rErrRef, const std::vector<OUString>& rTabNames,
        const ScSingleRefData& rRef, const ScAddress& rAbsRef,
        bool bForceTab, bool bODF, SingletonDisplay eSingletonDisplay )
    {
        if( rRef.IsFlag3D() || bForceTab )
        {
            if (!ValidTab(rAbsRef.Tab()) || rRef.IsTabDeleted())
            {
                if (!rRef.IsTabRel())
                    rBuffer.append('$');
                rBuffer.append(rErrRef);
                rBuffer.append('.');
            }
            else
            {
                if (!rRef.IsTabRel())
                    rBuffer.append('$');
                MakeTabStr(rBuffer, rTabNames, rAbsRef.Tab());
            }
        }
        else if (bODF)
            rBuffer.append('.');

        if (eSingletonDisplay != SINGLETON_ROW)
        {
            if (!rRef.IsColRel())
                rBuffer.append('$');
            if (!rLimits.ValidCol(rAbsRef.Col()) || rRef.IsColDeleted())
                rBuffer.append(rErrRef);
            else
                MakeColStr(rLimits, rBuffer, rAbsRef.Col());
        }

        if (eSingletonDisplay != SINGLETON_COL)
        {
            if (!rRef.IsRowRel())
                rBuffer.append('$');
            if (!rLimits.ValidRow(rAbsRef.Row()) || rRef.IsRowDeleted())
                rBuffer.append(rErrRef);
            else
                MakeRowStr(rLimits, rBuffer, rAbsRef.Row());
        }
    }

    static SingletonDisplay getSingletonDisplay( const ScSheetLimits& rLimits, const ScAddress& rAbs1, const ScAddress& rAbs2,
            const ScComplexRefData& rRef, bool bFromRangeName )
    {
        // If any part is error, display as such.
        if (!rLimits.ValidCol(rAbs1.Col()) || rRef.Ref1.IsColDeleted() || !rLimits.ValidRow(rAbs1.Row()) || rRef.Ref1.IsRowDeleted() ||
            !rLimits.ValidCol(rAbs2.Col()) || rRef.Ref2.IsColDeleted() || !rLimits.ValidRow(rAbs2.Row()) || rRef.Ref2.IsRowDeleted())
            return SINGLETON_NONE;

        // A:A or $A:$A or A:$A or $A:A
        if (rRef.IsEntireCol(rLimits))
            return SINGLETON_COL;

        // Same if not in named expression and both rows of entire columns are
        // relative references.
        if (!bFromRangeName && rAbs1.Row() == 0 && rAbs2.Row() == rLimits.mnMaxRow &&
                rRef.Ref1.IsRowRel() && rRef.Ref2.IsRowRel())
            return SINGLETON_COL;

        // 1:1 or $1:$1 or 1:$1 or $1:1
        if (rRef.IsEntireRow(rLimits))
            return SINGLETON_ROW;

        // Same if not in named expression and both columns of entire rows are
        // relative references.
        if (!bFromRangeName && rAbs1.Col() == 0 && rAbs2.Col() == rLimits.mnMaxCol &&
                rRef.Ref1.IsColRel() && rRef.Ref2.IsColRel())
            return SINGLETON_ROW;

        return SINGLETON_NONE;
    }

    virtual void makeRefStr(
                     ScSheetLimits& rLimits,
                     OUStringBuffer&   rBuffer,
                     formula::FormulaGrammar::Grammar /*eGram*/,
                     const ScAddress& rPos,
                     const OUString& rErrRef, const std::vector<OUString>& rTabNames,
                     const ScComplexRefData& rRef,
                     bool bSingleRef,
                     bool bFromRangeName ) const override
    {
        // In case absolute/relative positions weren't separately available:
        // transform relative to absolute!
        ScAddress aAbs1 = rRef.Ref1.toAbs(rLimits, rPos), aAbs2;
        if( !bSingleRef )
            aAbs2 = rRef.Ref2.toAbs(rLimits, rPos);

        SingletonDisplay eSingleton = bSingleRef ? SINGLETON_NONE :
            getSingletonDisplay( rLimits, aAbs1, aAbs2, rRef, bFromRangeName);
        MakeOneRefStrImpl(rLimits, rBuffer, rErrRef, rTabNames, rRef.Ref1, aAbs1, falsefalseeSingleton);
        if (!bSingleRef)
        {
            rBuffer.append(':');
            MakeOneRefStrImpl(rLimits, rBuffer, rErrRef, rTabNames, rRef.Ref2, aAbs2, aAbs1.Tab() != aAbs2.Tab(), false,
                    eSingleton);
        }
    }

    virtual sal_Unicode getSpecialSymbol( SpecialSymbolType eSymType ) const override
    {
        switch (eSymType)
        {
            case ScCompiler::Convention::ABS_SHEET_PREFIX:
                return '$';
            case ScCompiler::Convention::SHEET_SEPARATOR:
                return '.';
        }

        return u'\0';
    }

    virtual bool parseExternalName( const OUString& rSymbol, OUString& rFile, OUString& rName,
            const ScDocument& rDoc,
            const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks ) const override
    {
        return lcl_parseExternalName(rSymbol, rFile, rName, '#', rDoc, pExternalLinks);
    }

    virtual OUString makeExternalNameStr( sal_uInt16 /*nFileId*/, const OUString& rFile,
            const OUString& rName ) const override
    {
        return lcl_makeExternalNameStr( rFile, rName, '#'false);
    }

    static bool makeExternalSingleRefStr(
        const ScSheetLimits& rLimits,
        OUStringBuffer& rBuffer, const OUString& rFileName, const OUString& rTabName,
        const ScSingleRefData& rRef, const ScAddress& rPos, bool bDisplayTabName, bool bEncodeUrl )
    {
        ScAddress aAbsRef = rRef.toAbs(rLimits, rPos);
        if (bDisplayTabName)
        {
            OUString aFile;
            if (bEncodeUrl)
                aFile = rFileName;
            else
                aFile = INetURLObject::decode(rFileName, INetURLObject::DecodeMechanism::Unambiguous);

            rBuffer.append("'" + aFile.replaceAll("'""''") + "'#");

            if (!rRef.IsTabRel())
                rBuffer.append('$');
            ScRangeStringConverter::AppendTableName(rBuffer, rTabName);

            rBuffer.append('.');
        }

        if (!rRef.IsColRel())
            rBuffer.append('$');
        MakeColStr( rLimits, rBuffer, aAbsRef.Col());
        if (!rRef.IsRowRel())
            rBuffer.append('$');
        MakeRowStr( rLimits, rBuffer, aAbsRef.Row());

        return true;
    }

    static void makeExternalRefStrImpl(
        const ScSheetLimits& rLimits,
        OUStringBuffer& rBuffer, const ScAddress& rPos, const OUString& rFileName,
        const OUString& rTabName, const ScSingleRefData& rRef, bool bODF )
    {
        if (bODF)
            rBuffer.append( '[');

        bool bEncodeUrl = bODF;
        makeExternalSingleRefStr(rLimits, rBuffer, rFileName, rTabName, rRef, rPos, true, bEncodeUrl);
        if (bODF)
            rBuffer.append( ']');
    }

    virtual void makeExternalRefStr(
        ScSheetLimits& rLimits,
        OUStringBuffer& rBuffer, const ScAddress& rPos, sal_uInt16 /*nFileId*/, const OUString& rFileName,
        const OUString& rTabName, const ScSingleRefData& rRef ) const override
    {
        makeExternalRefStrImpl(rLimits, rBuffer, rPos, rFileName, rTabName, rRef, false);
    }

    static void makeExternalRefStrImpl(
        const ScSheetLimits& rLimits,
        OUStringBuffer& rBuffer, const ScAddress& rPos, const OUString& rFileName,
        const std::vector<OUString>& rTabNames, const OUString& rTabName,
        const ScComplexRefData& rRef, bool bODF )
    {
        ScRange aAbsRange = rRef.toAbs(rLimits, rPos);

        if (bODF)
            rBuffer.append( '[');
        // Ensure that there's always a closing bracket, no premature returns.
        bool bEncodeUrl = bODF;

        do
        {
            if (!makeExternalSingleRefStr(rLimits, rBuffer, rFileName, rTabName, rRef.Ref1, rPos, true, bEncodeUrl))
                break;

            rBuffer.append(':');

            OUString aLastTabName;
            bool bDisplayTabName = (aAbsRange.aStart.Tab() != aAbsRange.aEnd.Tab());
            if (bDisplayTabName)
            {
                // Get the name of the last table.
                if (!lcl_getLastTabName(aLastTabName, rTabName, rTabNames, aAbsRange))
                {
                    OSL_FAIL( "ConventionOOO_A1::makeExternalRefStrImpl: sheet name not found");
                    // aLastTabName contains #REF!, proceed.
                }
            }
            else if (bODF)
                rBuffer.append( '.');      // need at least the sheet separator in ODF
            makeExternalSingleRefStr(rLimits,
                rBuffer, rFileName, aLastTabName, rRef.Ref2, rPos, bDisplayTabName, bEncodeUrl);
        } while (false);

        if (bODF)
            rBuffer.append( ']');
    }

    virtual void makeExternalRefStr(
        ScSheetLimits& rLimits,
        OUStringBuffer& rBuffer, const ScAddress& rPos, sal_uInt16 /*nFileId*/, const OUString& rFileName,
        const std::vector<OUString>& rTabNames, const OUString& rTabName,
        const ScComplexRefData& rRef ) const override
    {
        makeExternalRefStrImpl(rLimits, rBuffer, rPos, rFileName, rTabNames, rTabName, rRef, false);
    }
};

struct ConventionOOO_A1_ODF : public ConventionOOO_A1
{
    ConventionOOO_A1_ODF() : ConventionOOO_A1 (FormulaGrammar::CONV_ODF) { }

    virtual void makeRefStr(
                     ScSheetLimits& rLimits,
                     OUStringBuffer&   rBuffer,
                     formula::FormulaGrammar::Grammar eGram,
                     const ScAddress& rPos,
                     const OUString& rErrRef, const std::vector<OUString>& rTabNames,
                     const ScComplexRefData& rRef,
                     bool bSingleRef,
                     bool bFromRangeName ) const override
    {
        rBuffer.append('[');
        // In case absolute/relative positions weren't separately available:
        // transform relative to absolute!
        ScAddress aAbs1 = rRef.Ref1.toAbs(rLimits, rPos), aAbs2;
        if( !bSingleRef )
            aAbs2 = rRef.Ref2.toAbs(rLimits, rPos);

        if (FormulaGrammar::isODFF(eGram) && (rRef.Ref1.IsDeleted() || !rLimits.ValidAddress(aAbs1) ||
                    (!bSingleRef && (rRef.Ref2.IsDeleted() || !rLimits.ValidAddress(aAbs2)))))
        {
            rBuffer.append(rErrRef);
            // For ODFF write [#REF!], but not for PODF so apps reading ODF
            // 1.0/1.1 may have a better chance if they implemented the old
            // form.
        }
        else
        {
            SingletonDisplay eSingleton = bSingleRef ? SINGLETON_NONE :
                getSingletonDisplay( rLimits, aAbs1, aAbs2, rRef, bFromRangeName);
            MakeOneRefStrImpl(rLimits, rBuffer, rErrRef, rTabNames, rRef.Ref1, aAbs1, falsetrue, eSingleton);
            if (!bSingleRef)
            {
                rBuffer.append(':');
                MakeOneRefStrImpl(rLimits, rBuffer, rErrRef, rTabNames, rRef.Ref2, aAbs2, aAbs1.Tab() != aAbs2.Tab(), true,
                        eSingleton);
            }
        }
        rBuffer.append(']');
    }

    virtual OUString makeExternalNameStr( sal_uInt16 /*nFileId*/, const OUString& rFile,
            const OUString& rName ) const override
    {
        return lcl_makeExternalNameStr( rFile, rName, '#'true);
    }

    virtual void makeExternalRefStr(
        ScSheetLimits& rLimits,
        OUStringBuffer& rBuffer, const ScAddress& rPos, sal_uInt16 /*nFileId*/, const OUString& rFileName,
        const OUString& rTabName, const ScSingleRefData& rRef ) const override
    {
        makeExternalRefStrImpl(rLimits, rBuffer, rPos, rFileName, rTabName, rRef, true);
    }

    virtual void makeExternalRefStr(
        ScSheetLimits& rLimits,
        OUStringBuffer& rBuffer, const ScAddress& rPos, sal_uInt16 /*nFileId*/, const OUString& rFileName,
        const std::vector<OUString>& rTabNames,
        const OUString& rTabName, const ScComplexRefData& rRef ) const override
    {
        makeExternalRefStrImpl(rLimits, rBuffer, rPos, rFileName, rTabNames, rTabName, rRef, true);
    }
};

struct ConventionXL
{
    virtual ~ConventionXL()
    {
    }

    static void GetTab(
        const ScSheetLimits& rLimits,
        const ScAddress& rPos, const std::vector<OUString>& rTabNames,
        const ScSingleRefData& rRef, OUString& rTabName )
    {
        ScAddress aAbs = rRef.toAbs(rLimits, rPos);
        if (rRef.IsTabDeleted() || o3tl::make_unsigned(aAbs.Tab()) >= rTabNames.size())
        {
            rTabName = ScResId( STR_NO_REF_TABLE );
            return;
        }
        rTabName = rTabNames[aAbs.Tab()];
    }

    static void MakeTabStr( const ScSheetLimits& rLimits, OUStringBuffer& rBuf,
                            const ScAddress& rPos,
                            const std::vector<OUString>& rTabNames,
                            const ScComplexRefData& rRef,
                            bool bSingleRef )
    {
        if( !rRef.Ref1.IsFlag3D() )
            return;

        OUString aStartTabName, aEndTabName;

        GetTab(rLimits, rPos, rTabNames, rRef.Ref1, aStartTabName);

        if( !bSingleRef && rRef.Ref2.IsFlag3D() )
        {
            GetTab(rLimits, rPos, rTabNames, rRef.Ref2, aEndTabName);
        }

        const sal_Int32 nQuotePos = rBuf.getLength();
        rBuf.append( aStartTabName );
        if( !bSingleRef && rRef.Ref2.IsFlag3D() && aStartTabName != aEndTabName )
        {
            ScCompiler::FormExcelSheetRange( rBuf, nQuotePos, aEndTabName);
        }

        rBuf.append( '!' );
    }

    static sal_Unicode getSpecialSymbol( ScCompiler::Convention::SpecialSymbolType eSymType )
    {
        switch (eSymType)
        {
            case ScCompiler::Convention::ABS_SHEET_PREFIX:
                return u'\0';
            case ScCompiler::Convention::SHEET_SEPARATOR:
                return '!';
        }
        return u'\0';
    }

    static bool parseExternalName( const OUString& rSymbol, OUString& rFile, OUString& rName,
            const ScDocument& rDoc,
            const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks )
    {
        return lcl_parseExternalName( rSymbol, rFile, rName, '!', rDoc, pExternalLinks);
    }

    static OUString makeExternalNameStr( const OUString& rFile, const OUString& rName )
    {
        return lcl_makeExternalNameStr( rFile, rName, '!'false);
    }

    static void makeExternalDocStr( OUStringBuffer& rBuffer, std::u16string_view rFullName )
    {
        // Format that is easier to deal with inside OOo, because we use file
        // URL, and all characters are allowed.  Check if it makes sense to do
        // it the way Gnumeric does it.  Gnumeric doesn't use the URL form
        // and allows relative file path.
        //
        //   ['file:///path/to/source/filename.xls']

        rBuffer.append('[');
        rBuffer.append('\'');
        OUString aFullName = INetURLObject::decode(rFullName, INetURLObject::DecodeMechanism::Unambiguous);

        const sal_Unicode* pBuf = aFullName.getStr();
        sal_Int32 nLen = aFullName.getLength();
        for (sal_Int32 i = 0; i < nLen; ++i)
        {
            const sal_Unicode c = pBuf[i];
            if (c == '\'')
                rBuffer.append(c);
            rBuffer.append(c);
        }
        rBuffer.append('\'');
        rBuffer.append(']');
    }

    static void makeExternalTabNameRange( OUStringBuffer& rBuf, const OUString& rTabName,
                                          const vector<OUString>& rTabNames,
                                          const ScRange& rRef )
    {
        OUString aLastTabName;
        if (!lcl_getLastTabName(aLastTabName, rTabName, rTabNames, rRef))
        {
            ScRangeStringConverter::AppendTableName(rBuf, aLastTabName);
            return;
        }

        ScRangeStringConverter::AppendTableName(rBuf, rTabName);
        if (rTabName != aLastTabName)
        {
            rBuf.append(':');
            ScRangeStringConverter::AppendTableName(rBuf, aLastTabName);
        }
    }

    virtual void parseExternalDocName( const OUString& rFormula, sal_Int32& rSrcPos const
    {
        sal_Int32 nLen = rFormula.getLength();
        const sal_Unicode* p = rFormula.getStr();
        sal_Unicode cPrev = 0;
        for (sal_Int32 i = rSrcPos; i < nLen; ++i)
        {
            sal_Unicode c = p[i];
            if (i == rSrcPos)
            {
                // first character must be '['.
                if (c != '[')
                    return;
            }
            else if (i == rSrcPos + 1)
            {
                // second character must be a single quote.
                if (c != '\'')
                    return;
            }
            else if (c == '\'')
            {
                if (cPrev == '\'')
                    // two successive single quote is treated as a single
                    // valid character.
                    c = 'a';
            }
            else if (c == ']')
            {
                if (cPrev == '\'')
                {
                    // valid source document path found.  Increment the
                    // current position to skip the source path.
                    rSrcPos = i + 1;
                    if (rSrcPos >= nLen)
                        rSrcPos = nLen - 1;
                    return;
                }
                else
                    return;
            }
            else
            {
                // any other character
                if (i > rSrcPos + 2 && cPrev == '\'')
                    // unless it's the 3rd character, a normal character
                    // following immediately a single quote is invalid.
                    return;
            }
            cPrev = c;
        }
    }
};

struct ConventionXL_A1 : public Convention_A1, public ConventionXL
{
    ConventionXL_A1() : Convention_A1( FormulaGrammar::CONV_XL_A1 ) { }
    explicit ConventionXL_A1( FormulaGrammar::AddressConvention eConv ) : Convention_A1( eConv ) { }

    static void makeSingleCellStr( const ScSheetLimits& rLimits, OUStringBuffer& rBuf, const ScSingleRefData& rRef, const ScAddress& rAbs )
    {
        if (!rRef.IsColRel())
            rBuf.append('$');
        MakeColStr(rLimits, rBuf, rAbs.Col());
        if (!rRef.IsRowRel())
            rBuf.append('$');
        MakeRowStr(rLimits, rBuf, rAbs.Row());
    }

    virtual void makeRefStr(
                     ScSheetLimits& rLimits,
                     OUStringBuffer&   rBuf,
                     formula::FormulaGrammar::Grammar /*eGram*/,
                     const ScAddress& rPos,
                     const OUString& rErrRef, const std::vector<OUString>& rTabNames,
                     const ScComplexRefData& rRef,
                     bool bSingleRef,
                     bool /*bFromRangeName*/ ) const override
    {
        ScComplexRefData aRef( rRef );

        // Play fast and loose with invalid refs.  There is not much point in producing
        // Foo!A1:#REF! versus #REF! at this point
        ScAddress aAbs1 = aRef.Ref1.toAbs(rLimits, rPos), aAbs2;

        MakeTabStr(rLimits, rBuf, rPos, rTabNames, aRef, bSingleRef);

        if (!rLimits.ValidAddress(aAbs1))
        {
            rBuf.append(rErrRef);
            return;
        }

        if( !bSingleRef )
        {
            aAbs2 = aRef.Ref2.toAbs(rLimits, rPos);
            if (!rLimits.ValidAddress(aAbs2))
            {
                rBuf.append(rErrRef);
                return;
            }

            if (aAbs1.Col() == 0 && aAbs2.Col() >= rLimits.mnMaxCol)
            {
                if (!aRef.Ref1.IsRowRel())
                    rBuf.append( '$' );
                MakeRowStr(rLimits, rBuf, aAbs1.Row());
                rBuf.append( ':' );
                if (!aRef.Ref2.IsRowRel())
                    rBuf.append( '$' );
                MakeRowStr(rLimits, rBuf, aAbs2.Row());
                return;
            }

            if (aAbs1.Row() == 0 && aAbs2.Row() >= rLimits.mnMaxRow)
            {
                if (!aRef.Ref1.IsColRel())
                    rBuf.append( '$' );
                MakeColStr(rLimits, rBuf, aAbs1.Col());
                rBuf.append( ':' );
                if (!aRef.Ref2.IsColRel())
                    rBuf.append( '$' );
                MakeColStr(rLimits, rBuf, aAbs2.Col());
                return;
            }
        }

        makeSingleCellStr(rLimits, rBuf, aRef.Ref1, aAbs1);
        if (!bSingleRef &&
                (aAbs1.Row() != aAbs2.Row() || aRef.Ref1.IsRowRel() != aRef.Ref2.IsRowRel() ||
                 aAbs1.Col() != aAbs2.Col() || aRef.Ref1.IsColRel() != aRef.Ref2.IsColRel()))
        {
            rBuf.append( ':' );
            makeSingleCellStr(rLimits, rBuf, aRef.Ref2, aAbs2);
        }
    }

    virtual ParseResult parseAnyToken( const OUString& rFormula,
                                       sal_Int32 nSrcPos,
                                       const CharClass* pCharClass,
                                       bool bGroupSeparator) const override
    {
        parseExternalDocName(rFormula, nSrcPos);

        ParseResult aRet;
        if ( lcl_isValidQuotedText(rFormula, nSrcPos, aRet) )
            return aRet;

        constexpr sal_Int32 nStartFlags = KParseTokens::ANY_LETTER_OR_NUMBER |
            KParseTokens::ASC_UNDERSCORE | KParseTokens::ASC_DOLLAR;
        constexpr sal_Int32 nContFlags = nStartFlags | KParseTokens::ASC_DOT;
        // '?' allowed in range names
        static constexpr OUString aAddAllowed(u"?!"_ustr);
        return pCharClass->parseAnyToken( rFormula,
                nSrcPos, nStartFlags, aAddAllowed,
                (bGroupSeparator ? nContFlags | KParseTokens::GROUP_SEPARATOR_IN_NUMBER_3 : nContFlags),
                aAddAllowed );
    }

    virtual sal_Unicode getSpecialSymbol( SpecialSymbolType eSymType ) const override
    {
        return ConventionXL::getSpecialSymbol(eSymType);
    }

    virtual bool parseExternalName( const OUString& rSymbol, OUString& rFile, OUString& rName,
            const ScDocument& rDoc,
            const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks ) const override
    {
        return ConventionXL::parseExternalName( rSymbol, rFile, rName, rDoc, pExternalLinks);
    }

    virtual OUString makeExternalNameStr( sal_uInt16 /*nFileId*/, const OUString& rFile,
            const OUString& rName ) const override
    {
        return ConventionXL::makeExternalNameStr(rFile, rName);
    }

    virtual void makeExternalRefStr(
        ScSheetLimits& rLimits,
        OUStringBuffer& rBuffer, const ScAddress& rPos, sal_uInt16 /*nFileId*/, const OUString& rFileName,
        const OUString& rTabName, const ScSingleRefData& rRef ) const override
    {
        // ['file:///path/to/file/filename.xls']'Sheet Name'!$A$1
        // This is a little different from the format Excel uses, as Excel
        // puts [] only around the file name.  But we need to enclose the
        // whole file path with [] because the file name can contain any
        // characters.

        ConventionXL::makeExternalDocStr(rBuffer, rFileName);
        ScRangeStringConverter::AppendTableName(rBuffer, rTabName);
        rBuffer.append('!');

        makeSingleCellStr(rLimits, rBuffer, rRef, rRef.toAbs(rLimits, rPos));
    }

    virtual void makeExternalRefStr(
        ScSheetLimits& rLimits,
        OUStringBuffer& rBuffer, const ScAddress& rPos, sal_uInt16 /*nFileId*/, const OUString& rFileName,
        const std::vector<OUString>& rTabNames, const OUString& rTabName,
        const ScComplexRefData& rRef ) const override
    {
        ScRange aAbsRef = rRef.toAbs(rLimits, rPos);

        ConventionXL::makeExternalDocStr(rBuffer, rFileName);
        ConventionXL::makeExternalTabNameRange(rBuffer, rTabName, rTabNames, aAbsRef);
        rBuffer.append('!');

        makeSingleCellStr(rLimits, rBuffer, rRef.Ref1, aAbsRef.aStart);
        if (aAbsRef.aStart != aAbsRef.aEnd)
        {
            rBuffer.append(':');
            makeSingleCellStr(rLimits, rBuffer, rRef.Ref2, aAbsRef.aEnd);
        }
    }
};

struct ConventionXL_OOX : public ConventionXL_A1
{
    ConventionXL_OOX() : ConventionXL_A1( FormulaGrammar::CONV_XL_OOX ) { }

    virtual void makeRefStr( ScSheetLimits& rLimits,
                     OUStringBuffer&   rBuf,
                     formula::FormulaGrammar::Grammar eGram,
                     const ScAddress& rPos,
                     const OUString& rErrRef, const std::vector<OUString>& rTabNames,
                     const ScComplexRefData& rRef,
                     bool bSingleRef,
                     bool bFromRangeName ) const override
    {
        // In OOXML relative references in named expressions are relative to
        // column 0 and row 0. Relative sheet references don't exist.
        ScAddress aPos( rPos );
        if (bFromRangeName)
        {
            // XXX NOTE: by decrementing the reference position we may end up
            // with resolved references with negative values. There's no proper
            // way to solve that or wrap them around without sheet dimensions
            // that are stored along. That, or blindly assume fixed dimensions
            // here and in import.
            /* TODO: maybe do that blind fixed dimensions wrap? */
            aPos.SetCol(0);
            aPos.SetRow(0);
        }

        if (rRef.Ref1.IsDeleted() || (!bSingleRef && rRef.Ref2.IsDeleted()))
        {
            // For OOXML write plain "#REF!" instead of detailed sheet/col/row
            // information.
            rBuf.append(rErrRef);
            return;
        }

        {
            ScAddress aAbs1 = rRef.Ref1.toAbs(rLimits, rPos);
            if (!rLimits.ValidAddress(aAbs1)
                || o3tl::make_unsigned(aAbs1.Tab()) >= rTabNames.size())
            {
                rBuf.append(rErrRef);
                return;
            }
        }

        if (!bSingleRef)
        {
            ScAddress aAbs2 = rRef.Ref2.toAbs(rLimits, rPos);
            if (!rLimits.ValidAddress(aAbs2)
                || o3tl::make_unsigned(aAbs2.Tab()) >= rTabNames.size())
            {
                rBuf.append(rErrRef);
                return;
            }
        }

        ConventionXL_A1::makeRefStr( rLimits, rBuf, eGram, aPos, rErrRef, rTabNames, rRef, bSingleRef, bFromRangeName);
    }

    virtual OUString makeExternalNameStr( sal_uInt16 nFileId, const OUString& /*rFile*/,
            const OUString& rName ) const override
    {
        // [N]!DefinedName is a workbook global name.
        return OUString( "[" + OUString::number(nFileId+1) + "]!" + rName );

        /* TODO: add support for sheet local names, would be
         * [N]'Sheet Name'!DefinedName
         * Similar to makeExternalRefStr() but with DefinedName instead of
         * CellStr. */

    }

    virtual void parseExternalDocName(const OUString& rFormula, sal_Int32& rSrcPosconst override
    {
        sal_Int32 nLen = rFormula.getLength();
        const sal_Unicode* p = rFormula.getStr();
        for (sal_Int32 i = rSrcPos; i < nLen; ++i)
        {
            sal_Unicode c = p[i];
            if (i == rSrcPos)
            {
                // first character must be '['.
                if (c != '[')
                    return;
            }
            else if (c == ']')
            {
                rSrcPos = i + 1;
                return;
            }
        }
    }

    virtual void makeExternalRefStr(
        ScSheetLimits& rLimits,
        OUStringBuffer& rBuffer, const ScAddress& rPos, sal_uInt16 nFileId, const OUString& /*rFileName*/,
        const OUString& rTabName, const ScSingleRefData& rRef ) const override
    {
        // '[N]Sheet Name'!$A$1 or [N]SheetName!$A$1
        // Where N is a 1-based positive integer number of a file name in OOXML
        // xl/externalLinks/externalLinkN.xml

        OUString aQuotedTab( rTabName);
        ScCompiler::CheckTabQuotes( aQuotedTab);
        if (!aQuotedTab.isEmpty() && aQuotedTab[0] == '\'')
        {
            rBuffer.append('\'');
            ConventionXL_OOX::makeExternalDocStr( rBuffer, nFileId);
            rBuffer.append( aQuotedTab.subView(1));
        }
        else
        {
            ConventionXL_OOX::makeExternalDocStr( rBuffer, nFileId);
            rBuffer.append( aQuotedTab);
        }
        rBuffer.append('!');

        makeSingleCellStr(rLimits, rBuffer, rRef, rRef.toAbs(rLimits, rPos));
    }

    virtual void makeExternalRefStr(
        ScSheetLimits& rLimits,
        OUStringBuffer& rBuffer, const ScAddress& rPos, sal_uInt16 nFileId, const OUString& /*rFileName*/,
        const std::vector<OUString>& rTabNames, const OUString& rTabName,
        const ScComplexRefData& rRef ) const override
    {
        // '[N]Sheet One':'Sheet Two'!A1:B2 or [N]SheetOne!A1:B2
        // Actually Excel writes '[N]Sheet One:Sheet Two'!A1:B2 but reads the
        // simpler to produce and more logical form with independently quoted
        // sheet names as well. The [N] having to be within the quoted sheet
        // name is ugly enough...

        ScRange aAbsRef = rRef.toAbs(rLimits, rPos);

        OUStringBuffer aBuf;
        ConventionXL::makeExternalTabNameRange( aBuf, rTabName, rTabNames, aAbsRef);
        if (!aBuf.isEmpty() && aBuf[0] == '\'')
        {
            rBuffer.append('\'');
            ConventionXL_OOX::makeExternalDocStr( rBuffer, nFileId);
            rBuffer.append( aBuf.subView(1));
        }
        else
        {
            ConventionXL_OOX::makeExternalDocStr( rBuffer, nFileId);
            rBuffer.append( aBuf);
        }
        rBuffer.append('!');

        makeSingleCellStr(rLimits, rBuffer, rRef.Ref1, aAbsRef.aStart);
        if (aAbsRef.aStart != aAbsRef.aEnd)
        {
            rBuffer.append(':');
            makeSingleCellStr(rLimits, rBuffer, rRef.Ref2, aAbsRef.aEnd);
        }
    }

    static void makeExternalDocStr( OUStringBuffer& rBuffer, sal_uInt16 nFileId )
    {
        rBuffer.append("[" + OUString::number( static_cast<sal_Int32>(nFileId+1) ) + "]");
    }
};

}

static void
r1c1_add_col( OUStringBuffer &rBuf, const ScSingleRefData& rRef, const ScAddress& ;rAbsRef )
{
    rBuf.append( 'C' );
    if( rRef.IsColRel() )
    {
        SCCOL nCol = rRef.Col();
        if (nCol != 0)
            rBuf.append("[" + OUString::number(nCol) + "]");
    }
    else
        rBuf.append( static_cast<sal_Int32>(rAbsRef.Col() + 1) );
}
static void
r1c1_add_row( OUStringBuffer &rBuf, const ScSingleRefData& rRef, const ScAddress& ;rAbsRef )
{
    rBuf.append( 'R' );
    if( rRef.IsRowRel() )
    {
        if (rRef.Row() != 0)
        {
            rBuf.append("[" + OUString::number(rRef.Row()) + "]");
        }
    }
    else
        rBuf.append( rAbsRef.Row() + 1 );
}

namespace {

struct ConventionXL_R1C1 : public ScCompiler::Convention, public ConventionXL
{
    ConventionXL_R1C1() : ScCompiler::Convention( FormulaGrammar::CONV_XL_R1C1 ) { }

    virtual void makeRefStr( ScSheetLimits& rLimits,
                     OUStringBuffer&   rBuf,
                     formula::FormulaGrammar::Grammar /*eGram*/,
                     const ScAddress& rPos,
                     const OUString& rErrRef, const std::vector<OUString>& rTabNames,
                     const ScComplexRefData& rRef,
                     bool bSingleRef,
                     bool /*bFromRangeName*/ ) const override
    {
        ScRange aAbsRef = rRef.toAbs(rLimits, rPos);
        ScComplexRefData aRef( rRef );

        MakeTabStr(rLimits, rBuf, rPos, rTabNames, aRef, bSingleRef);

        // Play fast and loose with invalid refs.  There is not much point in producing
        // Foo!A1:#REF! versus #REF! at this point
        if (!rLimits.ValidCol(aAbsRef.aStart.Col()) || !rLimits.ValidRow(aAbsRef.aStart.Row()))
        {
            rBuf.append(rErrRef);
            return;
        }

        if( !bSingleRef )
        {
            if (!rLimits.ValidCol(aAbsRef.aEnd.Col()) || !rLimits.ValidRow(aAbsRef.aEnd.Row()))
            {
                rBuf.append(rErrRef);
                return;
            }

            if (aAbsRef.aStart.Col() == 0 && aAbsRef.aEnd.Col() >= rLimits.mnMaxCol)
            {
                r1c1_add_row(rBuf,  rRef.Ref1, aAbsRef.aStart);
                if (aAbsRef.aStart.Row() != aAbsRef.aEnd.Row() ||
                    rRef.Ref1.IsRowRel() != rRef.Ref2.IsRowRel() )
                {
                    rBuf.append( ':' );
                    r1c1_add_row(rBuf,  rRef.Ref2, aAbsRef.aEnd);
                }
                return;

            }

            if (aAbsRef.aStart.Row() == 0 && aAbsRef.aEnd.Row() >= rLimits.mnMaxRow)
            {
                r1c1_add_col(rBuf, rRef.Ref1, aAbsRef.aStart);
                if (aAbsRef.aStart.Col() != aAbsRef.aEnd.Col() ||
                    rRef.Ref1.IsColRel() != rRef.Ref2.IsColRel())
                {
                    rBuf.append( ':' );
                    r1c1_add_col(rBuf, rRef.Ref2, aAbsRef.aEnd);
                }
                return;
            }
        }

        r1c1_add_row(rBuf, rRef.Ref1, aAbsRef.aStart);
        r1c1_add_col(rBuf, rRef.Ref1, aAbsRef.aStart);
        // We can't parse a single col/row reference in the context of a R1C1
        // 3D reference back yet, otherwise (if Excel understands it) an
        // additional condition similar to ConventionXL_A1::makeRefStr() could
        // be
        //
        //   && (aAbsRef.aStart.Row() != aAbsRef.aEnd.Row() || rRef.Ref1.IsRowRel() != rRef.Ref2.IsRowRel() ||
        //       aAbsRef.aStart.Col() != aAbsRef.aEnd.Col() || rRef.Ref1.IsColRel() != rRef.Ref2.IsColRel())
        if (!bSingleRef)
        {
            rBuf.append( ':' );
            r1c1_add_row(rBuf, rRef.Ref2, aAbsRef.aEnd);
            r1c1_add_col(rBuf, rRef.Ref2, aAbsRef.aEnd);
        }
    }

    ParseResult parseAnyToken( const OUString& rFormula,
                               sal_Int32 nSrcPos,
                               const CharClass* pCharClass,
                               bool bGroupSeparator) const override
    {
        parseExternalDocName(rFormula, nSrcPos);

        ParseResult aRet;
        if ( lcl_isValidQuotedText(rFormula, nSrcPos, aRet) )
            return aRet;

        constexpr sal_Int32 nStartFlags = KParseTokens::ANY_LETTER_OR_NUMBER |
            KParseTokens::ASC_UNDERSCORE ;
        constexpr sal_Int32 nContFlags = nStartFlags | KParseTokens::ASC_DOT;
        // '?' allowed in range names
        static constexpr OUString aAddAllowed(u"?-[]!"_ustr);

        return pCharClass->parseAnyToken( rFormula,
                nSrcPos, nStartFlags, aAddAllowed,
                (bGroupSeparator ? nContFlags | KParseTokens::GROUP_SEPARATOR_IN_NUMBER_3 : nContFlags),
                aAddAllowed );
    }

    virtual sal_Unicode getSpecialSymbol( SpecialSymbolType eSymType ) const override
    {
        return ConventionXL::getSpecialSymbol(eSymType);
    }

    virtual bool parseExternalName( const OUString& rSymbol, OUString& rFile, OUString& rName,
            const ScDocument& rDoc,
            const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks ) const override
    {
        return ConventionXL::parseExternalName( rSymbol, rFile, rName, rDoc, pExternalLinks);
    }

    virtual OUString makeExternalNameStr( sal_uInt16 /*nFileId*/, const OUString& rFile,
            const OUString& rName ) const override
    {
        return ConventionXL::makeExternalNameStr(rFile, rName);
    }

    virtual void makeExternalRefStr(
        ScSheetLimits& rLimits,
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=91 H=96 G=93

¤ Dauer der Verarbeitung: 0.24 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge