Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/LibreOffice/sc/source/core/tool/   (Office von Apache Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 15 kB image not shown  

Quelle  reftokenhelper.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 <reftokenhelper.hxx>
#include <document.hxx>
#include <rangeutl.hxx>
#include <compiler.hxx>
#include <tokenarray.hxx>

#include <rtl/ustring.hxx>
#include <formula/grammar.hxx>
#include <formula/token.hxx>

#include <memory>

using namespace formula;

using ::std::vector;

void ScRefTokenHelper::compileRangeRepresentation(
    vector<ScTokenRef>& rRefTokens, const OUString& rRangeStr, ScDocument& rDoc,
    const sal_Unicode cSep, FormulaGrammar::Grammar eGrammar, bool bOnly3DRef)
{
    // #i107275# ignore parentheses
    OUString aRangeStr = rRangeStr;
    while( (aRangeStr.getLength() >= 2) && (aRangeStr[ 0 ] == '(') && (aRangeStr[ aRangeStr.getLength() - 1 ] == ')') )
        aRangeStr = aRangeStr.copy( 1, aRangeStr.getLength() - 2 );

    bool bFailure = false;
    sal_Int32 nOffset = 0;
    while (nOffset >= 0 && !bFailure)
    {
        OUString aToken;
        ScRangeStringConverter::GetTokenByOffset(aToken, aRangeStr, nOffset, cSep);
        if (nOffset < 0)
            break;

        ScCompiler aCompiler(rDoc, ScAddress(0,0,0), eGrammar);
        std::unique_ptr<ScTokenArray> pArray(aCompiler.CompileString(aToken));

        // There MUST be exactly one reference per range token and nothing
        // else, and it MUST be a valid reference, not some #REF!
        sal_uInt16 nLen = pArray->GetLen();
        if (!nLen)
            continue;   // Should a missing range really be allowed?
        if (nLen != 1)
        {
            bFailure = true;
            break;
        }

        const FormulaToken* p = pArray->FirstToken();
        if (!p)
        {
            bFailure = true;
            break;
        }

        switch (p->GetType())
        {
            case svSingleRef:
                {
                    const ScSingleRefData& rRef = *p->GetSingleRef();
                    if (!rRef.Valid(rDoc))
                        bFailure = true;
                    else if (bOnly3DRef && !rRef.IsFlag3D())
                        bFailure = true;
                }
                break;
            case svDoubleRef:
                {
                    const ScComplexRefData& rRef = *p->GetDoubleRef();
                    if (!rRef.Valid(rDoc))
                        bFailure = true;
                    else if (bOnly3DRef && !rRef.Ref1.IsFlag3D())
                        bFailure = true;
                }
                break;
            case svExternalSingleRef:
                {
                    if (!p->GetSingleRef()->ValidExternal(rDoc))
                        bFailure = true;
                }
                break;
            case svExternalDoubleRef:
                {
                    if (!p->GetDoubleRef()->ValidExternal(rDoc))
                        bFailure = true;
                }
                break;
            case svString:
                if (p->GetString().isEmpty())
                    bFailure = true;
                break;
            case svIndex:
                {
                    if (p->GetOpCode() == ocName)
                    {
                        ScRangeData* pNameRange = rDoc.FindRangeNameBySheetAndIndex(p->GetSheet(), p->GetIndex());
                        if (!pNameRange->HasReferences())
                            bFailure = true;
                    }
                }
                break;
            default:
                bFailure = true;
                break;
        }
        if (!bFailure)
            rRefTokens.emplace_back(p->Clone());

    }
    if (bFailure)
        rRefTokens.clear();
}

bool ScRefTokenHelper::getRangeFromToken(
    const ScDocument* pDoc,
    ScRange& rRange, const ScTokenRef& pToken, const ScAddress& rPos, bool bExternal)
{
    StackVar eType = pToken->GetType();
    switch (pToken->GetType())
    {
        case svSingleRef:
        case svExternalSingleRef:
        {
            if ((eType == svExternalSingleRef && !bExternal) ||
                (eType == svSingleRef && bExternal))
                return false;

            const ScSingleRefData& rRefData = *pToken->GetSingleRef();
            rRange.aStart = rRefData.toAbs(*pDoc, rPos);
            rRange.aEnd = rRange.aStart;
            return true;
        }
        case svDoubleRef:
        case svExternalDoubleRef:
        {
            if ((eType == svExternalDoubleRef && !bExternal) ||
                (eType == svDoubleRef && bExternal))
                return false;

            const ScComplexRefData& rRefData = *pToken->GetDoubleRef();
            rRange = rRefData.toAbs(*pDoc, rPos);
            return true;
        }
        case svIndex:
        {
            if (pToken->GetOpCode() == ocName)
            {
                ScRangeData* pNameRange = pDoc->FindRangeNameBySheetAndIndex(pToken->GetSheet(), pToken->GetIndex());
                if (pNameRange->IsReference(rRange, rPos))
                    return true;
            }
            return false;
        }
        default:
            ; // do nothing
    }
    return false;
}

void ScRefTokenHelper::getRangeListFromTokens(
    const ScDocument* pDoc, ScRangeList& rRangeList, const vector<ScTokenRef>& rTokens, const ScAddress& rPos)
{
    for (const auto& rToken : rTokens)
    {
        ScRange aRange;
        getRangeFromToken(pDoc, aRange, rToken, rPos);
        rRangeList.push_back(aRange);
    }
}

void ScRefTokenHelper::getTokenFromRange(const ScDocument* pDoc, ScTokenRef& pToken, const ScRange& rRange)
{
    ScComplexRefData aData;
    aData.InitRange(rRange);
    aData.Ref1.SetFlag3D(true);

    // Display sheet name on 2nd reference only when the 1st and 2nd refs are on
    // different sheets.
    aData.Ref2.SetFlag3D(rRange.aStart.Tab() != rRange.aEnd.Tab());

    pToken.reset(new ScDoubleRefToken(pDoc->GetSheetLimits(), aData));
}

void ScRefTokenHelper::getTokensFromRangeList(const ScDocument* pDoc, vector<ScTokenRef>& pTokens, const ScRangeList& rRanges)
{
    vector<ScTokenRef> aTokens;
    size_t nCount = rRanges.size();
    aTokens.reserve(nCount);
    for (size_t i = 0; i < nCount; ++i)
    {
        const ScRange & rRange = rRanges[i];
        ScTokenRef pToken;
        ScRefTokenHelper::getTokenFromRange(pDoc, pToken, rRange);
        aTokens.push_back(pToken);
    }
    pTokens.swap(aTokens);
}

bool ScRefTokenHelper::isRef(const ScTokenRef& pToken)
{
    switch (pToken->GetType())
    {
        case svSingleRef:
        case svDoubleRef:
        case svExternalSingleRef:
        case svExternalDoubleRef:
            return true;
        default:
            ;
    }
    return false;
}

bool ScRefTokenHelper::isExternalRef(const ScTokenRef& pToken)
{
    switch (pToken->GetType())
    {
        case svExternalSingleRef:
        case svExternalDoubleRef:
            return true;
        default:
            ;
    }
    return false;
}

bool ScRefTokenHelper::intersects(
    const ScDocument* pDoc,
    const vector<ScTokenRef>& rTokens, const ScTokenRef& pToken, const ScAddress& rPos)
{
    if (!isRef(pToken))
        return false;

    bool bExternal = isExternalRef(pToken);
    sal_uInt16 nFileId = bExternal ? pToken->GetIndex() : 0;

    ScRange aRange;
    getRangeFromToken(pDoc, aRange, pToken, rPos, bExternal);

    for (const ScTokenRef& p : rTokens)
    {
        if (!isRef(p))
            continue;

        if (bExternal != isExternalRef(p))
            continue;

        ScRange aRange2;
        getRangeFromToken(pDoc, aRange2, p, rPos, bExternal);

        if (bExternal && nFileId != p->GetIndex())
            // different external file
            continue;

        if (aRange.Intersects(aRange2))
            return true;
    }
    return false;
}

namespace {

class JoinRefTokenRanges
{
public:
    /**
     * Insert a new reference token into the existing list of reference tokens,
     * but in that process, try to join as many adjacent ranges as possible.
     *
     * @param rTokens existing list of reference tokens
     * @param rToken new token
     */

    void operator() (const ScDocument* pDoc, vector<ScTokenRef>& rTokens, const ScTokenRef&&nbsp;pToken, const ScAddress& rPos)
    {
        join(pDoc, rTokens, pToken, rPos);
    }

private:

    /**
     * Check two 1-dimensional ranges to see if they overlap each other.
     *
     * @param nMin1 min value of range 1
     * @param nMax1 max value of range 1
     * @param nMin2 min value of range 2
     * @param nMax2 max value of range 2
     * @param rNewMin min value of new range in case they overlap
     * @param rNewMax max value of new range in case they overlap
     */

    template<typename T>
    static bool overlaps(T nMin1, T nMax1, T nMin2, T nMax2, T& rNewMin, T& rNewMax)
    {
        bool bDisjoint1 = (nMin1 > nMax2) && (nMin1 - nMax2 > 1);
        bool bDisjoint2  = (nMin2 > nMax1) && (nMin2 - nMax1 > 1);
        if (bDisjoint1 || bDisjoint2)
            // These two ranges cannot be joined.  Move on.
            return false;

        T nMin = std::min(nMin1, nMin2);
        T nMax = std::max(nMax1, nMax2);

        rNewMin = nMin;
        rNewMax = nMax;

        return true;
    }

    void join(const ScDocument* pDoc, vector<ScTokenRef>& rTokens, const ScTokenRef& pToken, const ScAddress& rPos)
    {
        // Normalize the token to a double reference.
        ScComplexRefData aData;
        if (!ScRefTokenHelper::getDoubleRefDataFromToken(aData, pToken))
            return;

        // Get the information of the new token.
        bool bExternal = ScRefTokenHelper::isExternalRef(pToken);
        sal_uInt16 nFileId = bExternal ? pToken->GetIndex() : 0;
        svl::SharedString aTabName = bExternal ? pToken->GetString() : svl::SharedString::getEmptyString();

        bool bJoined = false;
        for (ScTokenRef& pOldToken : rTokens)
        {
            if (!ScRefTokenHelper::isRef(pOldToken))
                // A non-ref token should not have been added here in the first
                // place!
                continue;

            if (bExternal != ScRefTokenHelper::isExternalRef(pOldToken))
                // External and internal refs don't mix.
                continue;

            if (bExternal)
            {
                if (nFileId != pOldToken->GetIndex())
                    // Different external files.
                    continue;

                if (aTabName != pOldToken->GetString())
                    // Different table names.
                    continue;
            }

            ScComplexRefData aOldData;
            if (!ScRefTokenHelper::getDoubleRefDataFromToken(aOldData, pOldToken))
                continue;

            ScRange aOld = aOldData.toAbs(*pDoc, rPos), aNew = aData.toAbs(*pDoc, rPos);

            if (aNew.aStart.Tab() != aOld.aStart.Tab() || aNew.aEnd.Tab() != aOld.aEnd.Tab())
                // Sheet ranges differ.
                continue;

            if (aOld.Contains(aNew))
                // This new range is part of an existing range.  Skip it.
                return;

            bool bSameRows = (aNew.aStart.Row() == aOld.aStart.Row()) && (aNew.aEnd.Row() == aOld.aEnd.Row());
            bool bSameCols = (aNew.aStart.Col() == aOld.aStart.Col()) && (aNew.aEnd.Col() == aOld.aEnd.Col());
            ScComplexRefData aNewData = aOldData;
            bool bJoinRanges = false;
            if (bSameRows)
            {
                SCCOL nNewMin, nNewMax;
                bJoinRanges = overlaps(
                    aNew.aStart.Col(), aNew.aEnd.Col(), aOld.aStart.Col(), aOld.aEnd.Col(),
                    nNewMin, nNewMax);

                if (bJoinRanges)
                {
                    aNew.aStart.SetCol(nNewMin);
                    aNew.aEnd.SetCol(nNewMax);
                    aNewData.SetRange(pDoc->GetSheetLimits(), aNew, rPos);
                }
            }
            else if (bSameCols)
            {
                SCROW nNewMin, nNewMax;
                bJoinRanges = overlaps(
                    aNew.aStart.Row(), aNew.aEnd.Row(), aOld.aStart.Row(), aOld.aEnd.Row(),
                    nNewMin, nNewMax);

                if (bJoinRanges)
                {
                    aNew.aStart.SetRow(nNewMin);
                    aNew.aEnd.SetRow(nNewMax);
                    aNewData.SetRange(pDoc->GetSheetLimits(), aNew, rPos);
                }
            }

            if (bJoinRanges)
            {
                if (bExternal)
                    pOldToken.reset(new ScExternalDoubleRefToken(nFileId, aTabName, aNewData));
                else
                    pOldToken.reset(new ScDoubleRefToken(pDoc->GetSheetLimits(), aNewData));

                bJoined = true;
                break;
            }
        }

        if (bJoined)
        {
            if (rTokens.size() == 1)
                // There is only one left.  No need to do more joining.
                return;

            // Pop the last token from the list, and keep joining recursively.
            ScTokenRef p = rTokens.back();
            rTokens.pop_back();
            join(pDoc, rTokens, p, rPos);
        }
        else
            rTokens.push_back(pToken);
    }
};

}

void ScRefTokenHelper::join(const ScDocument* pDoc, vector<ScTokenRef>& rTokens, const ScTokenRef& pToken, const ScAddress& rPos)
{
    JoinRefTokenRanges join;
    join(pDoc, rTokens, pToken, rPos);
}

bool ScRefTokenHelper::getDoubleRefDataFromToken(ScComplexRefData& rData, const ScTokenRef& pToken)
{
    switch (pToken->GetType())
    {
        case svSingleRef:
        case svExternalSingleRef:
        {
            const ScSingleRefData& r = *pToken->GetSingleRef();
            rData.Ref1 = r;
            rData.Ref1.SetFlag3D(true);
            rData.Ref2 = r;
            rData.Ref2.SetFlag3D(false); // Don't display sheet name on second reference.
        }
        break;
        case svDoubleRef:
        case svExternalDoubleRef:
            rData = *pToken->GetDoubleRef();
        break;
        default:
            // Not a reference token.  Bail out.
            return false;
    }
    return true;
}

ScTokenRef ScRefTokenHelper::createRefToken(const ScDocument& rDoc, const ScAddress& rAddr)
{
    ScSingleRefData aRefData;
    aRefData.InitAddress(rAddr);
    ScTokenRef pRef(new ScSingleRefToken(rDoc.GetSheetLimits(), aRefData));
    return pRef;
}

ScTokenRef ScRefTokenHelper::createRefToken(const ScDocument& rDoc, const ScRange&&nbsp;rRange)
{
    ScComplexRefData aRefData;
    aRefData.InitRange(rRange);
    ScTokenRef pRef(new ScDoubleRefToken(rDoc.GetSheetLimits(), aRefData));
    return pRef;
}

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

Messung V0.5
C=95 H=92 G=93

¤ Dauer der Verarbeitung: 0.5 Sekunden  ¤

*© 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.