LCOV - code coverage report
Current view: top level - sc/source/core/tool - reftokenhelper.cxx (source / functions) Hit Total Coverage
Test: commit 10e77ab3ff6f4314137acd6e2702a6e5c1ce1fae Lines: 185 221 83.7 %
Date: 2014-11-03 Functions: 18 18 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
       2             : /*
       3             :  * This file is part of the LibreOffice project.
       4             :  *
       5             :  * This Source Code Form is subject to the terms of the Mozilla Public
       6             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       7             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
       8             :  *
       9             :  * This file incorporates work covered by the following license notice:
      10             :  *
      11             :  *   Licensed to the Apache Software Foundation (ASF) under one or more
      12             :  *   contributor license agreements. See the NOTICE file distributed
      13             :  *   with this work for additional information regarding copyright
      14             :  *   ownership. The ASF licenses this file to you under the Apache
      15             :  *   License, Version 2.0 (the "License"); you may not use this file
      16             :  *   except in compliance with the License. You may obtain a copy of
      17             :  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
      18             :  */
      19             : 
      20             : #include "reftokenhelper.hxx"
      21             : #include "document.hxx"
      22             : #include "rangeutl.hxx"
      23             : #include "compiler.hxx"
      24             : #include "tokenarray.hxx"
      25             : 
      26             : #include <rtl/ustring.hxx>
      27             : #include <formula/grammar.hxx>
      28             : #include <formula/token.hxx>
      29             : 
      30             : #include <boost/scoped_ptr.hpp>
      31             : 
      32             : using namespace formula;
      33             : 
      34             : using ::std::vector;
      35             : 
      36        5032 : void ScRefTokenHelper::compileRangeRepresentation(
      37             :     vector<ScTokenRef>& rRefTokens, const OUString& rRangeStr, ScDocument* pDoc,
      38             :     const sal_Unicode cSep, FormulaGrammar::Grammar eGrammar, bool bOnly3DRef)
      39             : {
      40        5032 :     const sal_Unicode cQuote = '\'';
      41             : 
      42             :     // #i107275# ignore parentheses
      43        5032 :     OUString aRangeStr = rRangeStr;
      44       10064 :     while( (aRangeStr.getLength() >= 2) && (aRangeStr[ 0 ] == '(') && (aRangeStr[ aRangeStr.getLength() - 1 ] == ')') )
      45           0 :         aRangeStr = aRangeStr.copy( 1, aRangeStr.getLength() - 2 );
      46             : 
      47        5032 :     bool bFailure = false;
      48        5032 :     sal_Int32 nOffset = 0;
      49       15172 :     while (nOffset >= 0 && !bFailure)
      50             :     {
      51       10120 :         OUString aToken;
      52       10120 :         ScRangeStringConverter::GetTokenByOffset(aToken, aRangeStr, nOffset, cSep, cQuote);
      53       10120 :         if (nOffset < 0)
      54        5012 :             break;
      55             : 
      56       10216 :         ScCompiler aCompiler(pDoc, ScAddress(0,0,0));
      57        5108 :         aCompiler.SetGrammar(eGrammar);
      58       10216 :         boost::scoped_ptr<ScTokenArray> pArray(aCompiler.CompileString(aToken));
      59             : 
      60             :         // There MUST be exactly one reference per range token and nothing
      61             :         // else, and it MUST be a valid reference, not some #REF!
      62        5108 :         sal_uInt16 nLen = pArray->GetLen();
      63        5108 :         if (!nLen)
      64           0 :             continue;   // Should a missing range really be allowed?
      65        5108 :         if (nLen != 1)
      66             :         {
      67           0 :             bFailure = true;
      68           0 :             break;
      69             :         }
      70             : 
      71        5108 :         pArray->Reset();
      72        5108 :         const FormulaToken* p = pArray->Next();
      73        5108 :         if (!p)
      74             :         {
      75           0 :             bFailure = true;
      76           0 :             break;
      77             :         }
      78             : 
      79        5108 :         switch (p->GetType())
      80             :         {
      81             :             case svSingleRef:
      82             :                 {
      83        2026 :                     const ScSingleRefData& rRef = *p->GetSingleRef();
      84        2026 :                     if (!rRef.Valid())
      85           0 :                         bFailure = true;
      86        2026 :                     else if (bOnly3DRef && !rRef.IsFlag3D())
      87           0 :                         bFailure = true;
      88             :                 }
      89        2026 :                 break;
      90             :             case svDoubleRef:
      91             :                 {
      92        3030 :                     const ScComplexRefData& rRef = *p->GetDoubleRef();
      93        3030 :                     if (!rRef.Valid())
      94           0 :                         bFailure = true;
      95        3030 :                     else if (bOnly3DRef && !rRef.Ref1.IsFlag3D())
      96           0 :                         bFailure = true;
      97             :                 }
      98        3030 :                 break;
      99             :             case svExternalSingleRef:
     100             :                 {
     101           0 :                     if (!p->GetSingleRef()->ValidExternal())
     102           0 :                         bFailure = true;
     103             :                 }
     104           0 :                 break;
     105             :             case svExternalDoubleRef:
     106             :                 {
     107           0 :                     if (!p->GetDoubleRef()->ValidExternal())
     108           0 :                         bFailure = true;
     109             :                 }
     110           0 :                 break;
     111             :             case svString:
     112          32 :                 if (p->GetString().isEmpty())
     113           0 :                     bFailure = true;
     114          32 :                 break;
     115             :             default:
     116          20 :                 bFailure = true;
     117          20 :                 break;
     118             :         }
     119        5108 :         if (!bFailure)
     120        5088 :             rRefTokens.push_back(ScTokenRef(p->Clone()));
     121             : 
     122        5108 :     }
     123        5032 :     if (bFailure)
     124          20 :         rRefTokens.clear();
     125        5032 : }
     126             : 
     127       30651 : bool ScRefTokenHelper::getRangeFromToken(
     128             :     ScRange& rRange, const ScTokenRef& pToken, const ScAddress& rPos, bool bExternal)
     129             : {
     130       30651 :     StackVar eType = pToken->GetType();
     131       30651 :     switch (pToken->GetType())
     132             :     {
     133             :         case svSingleRef:
     134             :         case svExternalSingleRef:
     135             :         {
     136       15848 :             if ((eType == svExternalSingleRef && !bExternal) ||
     137       15848 :                 (eType == svSingleRef && bExternal))
     138           0 :                 return false;
     139             : 
     140       15848 :             const ScSingleRefData& rRefData = *pToken->GetSingleRef();
     141       15848 :             rRange.aStart = rRefData.toAbs(rPos);
     142       15848 :             rRange.aEnd = rRange.aStart;
     143       15848 :             return true;
     144             :         }
     145             :         case svDoubleRef:
     146             :         case svExternalDoubleRef:
     147             :         {
     148       14781 :             if ((eType == svExternalDoubleRef && !bExternal) ||
     149       14781 :                 (eType == svDoubleRef && bExternal))
     150           0 :                 return false;
     151             : 
     152       14781 :             const ScComplexRefData& rRefData = *pToken->GetDoubleRef();
     153       14781 :             rRange = rRefData.toAbs(rPos);
     154       14781 :             return true;
     155             :         }
     156             :         default:
     157             :             ; // do nothing
     158             :     }
     159          22 :     return false;
     160             : }
     161             : 
     162       13615 : void ScRefTokenHelper::getRangeListFromTokens(
     163             :     ScRangeList& rRangeList, const vector<ScTokenRef>& rTokens, const ScAddress& rPos)
     164             : {
     165       13615 :     vector<ScTokenRef>::const_iterator itr = rTokens.begin(), itrEnd = rTokens.end();
     166       27368 :     for (; itr != itrEnd; ++itr)
     167             :     {
     168       13753 :         ScRange aRange;
     169       13753 :         getRangeFromToken(aRange, *itr, rPos);
     170       13753 :         rRangeList.Append(aRange);
     171             :     }
     172       13615 : }
     173             : 
     174          64 : void ScRefTokenHelper::getTokenFromRange(ScTokenRef& pToken, const ScRange& rRange)
     175             : {
     176             :     ScComplexRefData aData;
     177          64 :     aData.InitRange(rRange);
     178          64 :     aData.Ref1.SetFlag3D(true);
     179             : 
     180             :     // Display sheet name on 2nd reference only when the 1st and 2nd refs are on
     181             :     // different sheets.
     182          64 :     aData.Ref2.SetFlag3D(rRange.aStart.Tab() != rRange.aEnd.Tab());
     183             : 
     184          64 :     pToken.reset(new ScDoubleRefToken(aData));
     185          64 : }
     186             : 
     187          40 : void ScRefTokenHelper::getTokensFromRangeList(vector<ScTokenRef>& pTokens, const ScRangeList& rRanges)
     188             : {
     189          40 :     vector<ScTokenRef> aTokens;
     190          40 :     size_t nCount = rRanges.size();
     191          40 :     aTokens.reserve(nCount);
     192         104 :     for (size_t i = 0; i < nCount; ++i)
     193             :     {
     194          64 :         const ScRange* pRange = rRanges[i];
     195          64 :         if (!pRange)
     196             :             // failed.
     197          40 :             return;
     198             : 
     199          64 :         ScTokenRef pToken;
     200          64 :         ScRefTokenHelper::getTokenFromRange(pToken,* pRange);
     201          64 :         aTokens.push_back(pToken);
     202          64 :     }
     203          40 :     pTokens.swap(aTokens);
     204             : }
     205             : 
     206       15098 : bool ScRefTokenHelper::isRef(const ScTokenRef& pToken)
     207             : {
     208       15098 :     switch (pToken->GetType())
     209             :     {
     210             :         case svSingleRef:
     211             :         case svDoubleRef:
     212             :         case svExternalSingleRef:
     213             :         case svExternalDoubleRef:
     214       15098 :             return true;
     215             :         default:
     216             :             ;
     217             :     }
     218           0 :     return false;
     219             : }
     220             : 
     221       42320 : bool ScRefTokenHelper::isExternalRef(const ScTokenRef& pToken)
     222             : {
     223       42320 :     switch (pToken->GetType())
     224             :     {
     225             :         case svExternalSingleRef:
     226             :         case svExternalDoubleRef:
     227           0 :             return true;
     228             :         default:
     229             :             ;
     230             :     }
     231       42320 :     return false;
     232             : }
     233             : 
     234           8 : bool ScRefTokenHelper::intersects(
     235             :     const vector<ScTokenRef>& rTokens, const ScTokenRef& pToken, const ScAddress& rPos)
     236             : {
     237           8 :     if (!isRef(pToken))
     238           0 :         return false;
     239             : 
     240           8 :     bool bExternal = isExternalRef(pToken);
     241           8 :     sal_uInt16 nFileId = bExternal ? pToken->GetIndex() : 0;
     242             : 
     243           8 :     ScRange aRange;
     244           8 :     getRangeFromToken(aRange, pToken, rPos, bExternal);
     245             : 
     246           8 :     vector<ScTokenRef>::const_iterator itr = rTokens.begin(), itrEnd = rTokens.end();
     247          12 :     for (; itr != itrEnd; ++itr)
     248             :     {
     249           8 :         const ScTokenRef& p = *itr;
     250           8 :         if (!isRef(p))
     251           0 :             continue;
     252             : 
     253           8 :         if (bExternal != isExternalRef(p))
     254           0 :             continue;
     255             : 
     256           8 :         ScRange aRange2;
     257           8 :         getRangeFromToken(aRange2, p, rPos, bExternal);
     258             : 
     259           8 :         if (bExternal && nFileId != p->GetIndex())
     260             :             // different external file
     261           0 :             continue;
     262             : 
     263           8 :         if (aRange.Intersects(aRange2))
     264           4 :             return true;
     265             :     }
     266           4 :     return false;
     267             : }
     268             : 
     269             : namespace {
     270             : 
     271             : class JoinRefTokenRanges
     272             : {
     273             : public:
     274             :     /**
     275             :      * Insert a new reference token into the existing list of reference tokens,
     276             :      * but in that process, try to join as many adjacent ranges as possible.
     277             :      *
     278             :      * @param rTokens existing list of reference tokens
     279             :      * @param rToken new token
     280             :      */
     281       18370 :     void operator() (vector<ScTokenRef>& rTokens, const ScTokenRef& pToken, const ScAddress& rPos)
     282             :     {
     283       18370 :         join(rTokens, pToken, rPos);
     284       18370 :     }
     285             : 
     286             : private:
     287             : 
     288             :     /**
     289             :      * Check two 1-dimensional ranges to see if they overlap each other.
     290             :      *
     291             :      * @param nMin1 min value of range 1
     292             :      * @param nMax1 max value of range 1
     293             :      * @param nMin2 min value of range 2
     294             :      * @param nMax2 max value of range 2
     295             :      * @param rNewMin min value of new range in case they overlap
     296             :      * @param rNewMax max value of new range in case they overlap
     297             :      */
     298             :     template<typename T>
     299       10934 :     static bool overlaps(T nMin1, T nMax1, T nMin2, T nMax2, T& rNewMin, T& rNewMax)
     300             :     {
     301       10934 :         bool bDisjoint1 = (nMin1 > nMax2) && (nMin1 - nMax2 > 1);
     302       10934 :         bool bDisjoint2  = (nMin2 > nMax1) && (nMin2 - nMax1 > 1);
     303       10934 :         if (bDisjoint1 || bDisjoint2)
     304             :             // These two ranges cannot be joined.  Move on.
     305           0 :             return false;
     306             : 
     307       10934 :         T nMin = nMin1 < nMin2 ? nMin1 : nMin2;
     308       10934 :         T nMax = nMax1 > nMax2 ? nMax1 : nMax2;
     309             : 
     310       10934 :         rNewMin = nMin;
     311       10934 :         rNewMax = nMax;
     312             : 
     313       10934 :         return true;
     314             :     }
     315             : 
     316       19824 :     void join(vector<ScTokenRef>& rTokens, const ScTokenRef& pToken, const ScAddress& rPos)
     317             :     {
     318             :         // Normalize the token to a double reference.
     319             :         ScComplexRefData aData;
     320       19824 :         if (!ScRefTokenHelper::getDoubleRefDataFromToken(aData, pToken))
     321        9584 :             return;
     322             : 
     323             :         // Get the information of the new token.
     324       19772 :         bool bExternal = ScRefTokenHelper::isExternalRef(pToken);
     325       19772 :         sal_uInt16 nFileId = bExternal ? pToken->GetIndex() : 0;
     326       19772 :         OUString aTabName = bExternal ? pToken->GetString().getString() : OUString();
     327             : 
     328       19772 :         bool bJoined = false;
     329       19772 :         vector<ScTokenRef>::iterator itr = rTokens.begin(), itrEnd = rTokens.end();
     330       22634 :         for (; itr != itrEnd; ++itr)
     331             :         {
     332       13796 :             ScTokenRef& pOldToken = *itr;
     333             : 
     334       13796 :             if (!ScRefTokenHelper::isRef(pOldToken))
     335             :                 // A non-ref token should not have been added here in the first
     336             :                 // place!
     337           0 :                 continue;
     338             : 
     339       13796 :             if (bExternal != ScRefTokenHelper::isExternalRef(pOldToken))
     340             :                 // External and internal refs don't mix.
     341           0 :                 continue;
     342             : 
     343       13796 :             if (bExternal)
     344             :             {
     345           0 :                 if (nFileId != pOldToken->GetIndex())
     346             :                     // Different external files.
     347           0 :                     continue;
     348             : 
     349           0 :                 if (aTabName != pOldToken->GetString().getString())
     350             :                     // Different table names.
     351           0 :                     continue;
     352             :             }
     353             : 
     354             :             ScComplexRefData aOldData;
     355       13796 :             if (!ScRefTokenHelper::getDoubleRefDataFromToken(aOldData, pOldToken))
     356           0 :                 continue;
     357             : 
     358       13796 :             ScRange aOld = aOldData.toAbs(rPos), aNew = aData.toAbs(rPos);
     359             : 
     360       13796 :             if (aNew.aStart.Tab() != aOld.aStart.Tab() || aNew.aEnd.Tab() != aOld.aEnd.Tab())
     361             :                 // Sheet ranges differ.
     362           0 :                 continue;
     363             : 
     364       13796 :             if (aOld.In(aNew))
     365             :                 // This new range is part of an existing range.  Skip it.
     366           0 :                 return;
     367             : 
     368       13796 :             bool bSameRows = (aNew.aStart.Row() == aOld.aStart.Row()) && (aNew.aEnd.Row() == aOld.aEnd.Row());
     369       13796 :             bool bSameCols = (aNew.aStart.Col() == aOld.aStart.Col()) && (aNew.aEnd.Col() == aOld.aEnd.Col());
     370       13796 :             ScComplexRefData aNewData = aOldData;
     371       13796 :             bool bJoinRanges = false;
     372       13796 :             if (bSameRows)
     373             :             {
     374             :                 SCCOL nNewMin, nNewMax;
     375             :                 bJoinRanges = overlaps(
     376       19688 :                     aNew.aStart.Col(), aNew.aEnd.Col(), aOld.aStart.Col(), aOld.aEnd.Col(),
     377       19688 :                     nNewMin, nNewMax);
     378             : 
     379        4922 :                 if (bJoinRanges)
     380             :                 {
     381        4922 :                     aNew.aStart.SetCol(nNewMin);
     382        4922 :                     aNew.aEnd.SetCol(nNewMax);
     383        4922 :                     aNewData.SetRange(aNew, rPos);
     384             :                 }
     385             :             }
     386        8874 :             else if (bSameCols)
     387             :             {
     388             :                 SCROW nNewMin, nNewMax;
     389             :                 bJoinRanges = overlaps(
     390             :                     aNew.aStart.Row(), aNew.aEnd.Row(), aOld.aStart.Row(), aOld.aEnd.Row(),
     391        6012 :                     nNewMin, nNewMax);
     392             : 
     393        6012 :                 if (bJoinRanges)
     394             :                 {
     395        6012 :                     aNew.aStart.SetRow(nNewMin);
     396        6012 :                     aNew.aEnd.SetRow(nNewMax);
     397        6012 :                     aNewData.SetRange(aNew, rPos);
     398             :                 }
     399             :             }
     400             : 
     401       13796 :             if (bJoinRanges)
     402             :             {
     403       10934 :                 if (bExternal)
     404           0 :                     pOldToken.reset(new ScExternalDoubleRefToken(nFileId, aTabName, aNewData));
     405             :                 else
     406       10934 :                     pOldToken.reset(new ScDoubleRefToken(aNewData));
     407             : 
     408       10934 :                 bJoined = true;
     409       10934 :                 break;
     410             :             }
     411             :         }
     412             : 
     413       19772 :         if (bJoined)
     414             :         {
     415       10934 :             if (rTokens.size() == 1)
     416             :                 // There is only one left.  No need to do more joining.
     417        9480 :                 return;
     418             : 
     419             :             // Pop the last token from the list, and keep joining recursively.
     420        1454 :             ScTokenRef p = rTokens.back();
     421        1454 :             rTokens.pop_back();
     422        1454 :             join(rTokens, p, rPos);
     423             :         }
     424             :         else
     425        8838 :             rTokens.push_back(pToken);
     426             :     }
     427             : };
     428             : 
     429             : }
     430             : 
     431       18370 : void ScRefTokenHelper::join(vector<ScTokenRef>& rTokens, const ScTokenRef& pToken, const ScAddress& rPos)
     432             : {
     433             :     JoinRefTokenRanges join;
     434       18370 :     join(rTokens, pToken, rPos);
     435       18370 : }
     436             : 
     437       35590 : bool ScRefTokenHelper::getDoubleRefDataFromToken(ScComplexRefData& rData, const ScTokenRef& pToken)
     438             : {
     439       35590 :     switch (pToken->GetType())
     440             :     {
     441             :         case svSingleRef:
     442             :         case svExternalSingleRef:
     443             :         {
     444       20562 :             const ScSingleRefData& r = *pToken->GetSingleRef();
     445       20562 :             rData.Ref1 = r;
     446       20562 :             rData.Ref1.SetFlag3D(true);
     447       20562 :             rData.Ref2 = r;
     448       20562 :             rData.Ref2.SetFlag3D(false); // Don't display sheet name on second reference.
     449             :         }
     450       20562 :         break;
     451             :         case svDoubleRef:
     452             :         case svExternalDoubleRef:
     453       14974 :             rData = *pToken->GetDoubleRef();
     454       14974 :         break;
     455             :         default:
     456             :             // Not a reference token.  Bail out.
     457          54 :             return false;
     458             :     }
     459       35536 :     return true;
     460             : }
     461             : 
     462           4 : ScTokenRef ScRefTokenHelper::createRefToken(const ScAddress& rAddr)
     463             : {
     464             :     ScSingleRefData aRefData;
     465           4 :     aRefData.InitAddress(rAddr);
     466           4 :     ScTokenRef pRef(new ScSingleRefToken(aRefData));
     467           4 :     return pRef;
     468             : }
     469             : 
     470           2 : ScTokenRef ScRefTokenHelper::createRefToken(const ScRange& rRange)
     471             : {
     472             :     ScComplexRefData aRefData;
     473           2 :     aRefData.InitRange(rRange);
     474           2 :     ScTokenRef pRef(new ScDoubleRefToken(aRefData));
     475           2 :     return pRef;
     476         228 : }
     477             : 
     478             : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Generated by: LCOV version 1.10