LCOV - code coverage report
Current view: top level - sc/source/core/tool - reftokenhelper.cxx (source / functions) Hit Total Coverage
Test: commit c8344322a7af75b84dd3ca8f78b05543a976dfd5 Lines: 185 221 83.7 %
Date: 2015-06-13 12:38:46 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        2729 : void ScRefTokenHelper::compileRangeRepresentation(
      37             :     vector<ScTokenRef>& rRefTokens, const OUString& rRangeStr, ScDocument* pDoc,
      38             :     const sal_Unicode cSep, FormulaGrammar::Grammar eGrammar, bool bOnly3DRef)
      39             : {
      40        2729 :     const sal_Unicode cQuote = '\'';
      41             : 
      42             :     // #i107275# ignore parentheses
      43        2729 :     OUString aRangeStr = rRangeStr;
      44        5458 :     while( (aRangeStr.getLength() >= 2) && (aRangeStr[ 0 ] == '(') && (aRangeStr[ aRangeStr.getLength() - 1 ] == ')') )
      45           0 :         aRangeStr = aRangeStr.copy( 1, aRangeStr.getLength() - 2 );
      46             : 
      47        2729 :     bool bFailure = false;
      48        2729 :     sal_Int32 nOffset = 0;
      49        8218 :     while (nOffset >= 0 && !bFailure)
      50             :     {
      51        5440 :         OUString aToken;
      52        5440 :         ScRangeStringConverter::GetTokenByOffset(aToken, aRangeStr, nOffset, cSep, cQuote);
      53        5440 :         if (nOffset < 0)
      54        2680 :             break;
      55             : 
      56        5520 :         ScCompiler aCompiler(pDoc, ScAddress(0,0,0));
      57        2760 :         aCompiler.SetGrammar(eGrammar);
      58        5520 :         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        2760 :         sal_uInt16 nLen = pArray->GetLen();
      63        2760 :         if (!nLen)
      64           0 :             continue;   // Should a missing range really be allowed?
      65        2760 :         if (nLen != 1)
      66             :         {
      67           0 :             bFailure = true;
      68           0 :             break;
      69             :         }
      70             : 
      71        2760 :         pArray->Reset();
      72        2760 :         const FormulaToken* p = pArray->Next();
      73        2760 :         if (!p)
      74             :         {
      75           0 :             bFailure = true;
      76           0 :             break;
      77             :         }
      78             : 
      79        2760 :         switch (p->GetType())
      80             :         {
      81             :             case svSingleRef:
      82             :                 {
      83        1051 :                     const ScSingleRefData& rRef = *p->GetSingleRef();
      84        1051 :                     if (!rRef.Valid())
      85           0 :                         bFailure = true;
      86        1051 :                     else if (bOnly3DRef && !rRef.IsFlag3D())
      87           0 :                         bFailure = true;
      88             :                 }
      89        1051 :                 break;
      90             :             case svDoubleRef:
      91             :                 {
      92        1644 :                     const ScComplexRefData& rRef = *p->GetDoubleRef();
      93        1644 :                     if (!rRef.Valid())
      94           0 :                         bFailure = true;
      95        1644 :                     else if (bOnly3DRef && !rRef.Ref1.IsFlag3D())
      96           0 :                         bFailure = true;
      97             :                 }
      98        1644 :                 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          16 :                 if (p->GetString().isEmpty())
     113           0 :                     bFailure = true;
     114          16 :                 break;
     115             :             default:
     116          49 :                 bFailure = true;
     117          49 :                 break;
     118             :         }
     119        2760 :         if (!bFailure)
     120        2711 :             rRefTokens.push_back(ScTokenRef(p->Clone()));
     121             : 
     122        2760 :     }
     123        2729 :     if (bFailure)
     124          49 :         rRefTokens.clear();
     125        2729 : }
     126             : 
     127       17580 : bool ScRefTokenHelper::getRangeFromToken(
     128             :     ScRange& rRange, const ScTokenRef& pToken, const ScAddress& rPos, bool bExternal)
     129             : {
     130       17580 :     StackVar eType = pToken->GetType();
     131       17580 :     switch (pToken->GetType())
     132             :     {
     133             :         case svSingleRef:
     134             :         case svExternalSingleRef:
     135             :         {
     136        8895 :             if ((eType == svExternalSingleRef && !bExternal) ||
     137        8895 :                 (eType == svSingleRef && bExternal))
     138           0 :                 return false;
     139             : 
     140        8895 :             const ScSingleRefData& rRefData = *pToken->GetSingleRef();
     141        8895 :             rRange.aStart = rRefData.toAbs(rPos);
     142        8895 :             rRange.aEnd = rRange.aStart;
     143        8895 :             return true;
     144             :         }
     145             :         case svDoubleRef:
     146             :         case svExternalDoubleRef:
     147             :         {
     148        8674 :             if ((eType == svExternalDoubleRef && !bExternal) ||
     149        8674 :                 (eType == svDoubleRef && bExternal))
     150           0 :                 return false;
     151             : 
     152        8674 :             const ScComplexRefData& rRefData = *pToken->GetDoubleRef();
     153        8674 :             rRange = rRefData.toAbs(rPos);
     154        8674 :             return true;
     155             :         }
     156             :         default:
     157             :             ; // do nothing
     158             :     }
     159          11 :     return false;
     160             : }
     161             : 
     162        7719 : void ScRefTokenHelper::getRangeListFromTokens(
     163             :     ScRangeList& rRangeList, const vector<ScTokenRef>& rTokens, const ScAddress& rPos)
     164             : {
     165        7719 :     vector<ScTokenRef>::const_iterator itr = rTokens.begin(), itrEnd = rTokens.end();
     166       15450 :     for (; itr != itrEnd; ++itr)
     167             :     {
     168        7731 :         ScRange aRange;
     169        7731 :         getRangeFromToken(aRange, *itr, rPos);
     170        7731 :         rRangeList.Append(aRange);
     171             :     }
     172        7719 : }
     173             : 
     174          32 : void ScRefTokenHelper::getTokenFromRange(ScTokenRef& pToken, const ScRange& rRange)
     175             : {
     176             :     ScComplexRefData aData;
     177          32 :     aData.InitRange(rRange);
     178          32 :     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          32 :     aData.Ref2.SetFlag3D(rRange.aStart.Tab() != rRange.aEnd.Tab());
     183             : 
     184          32 :     pToken.reset(new ScDoubleRefToken(aData));
     185          32 : }
     186             : 
     187          49 : void ScRefTokenHelper::getTokensFromRangeList(vector<ScTokenRef>& pTokens, const ScRangeList& rRanges)
     188             : {
     189          49 :     vector<ScTokenRef> aTokens;
     190          49 :     size_t nCount = rRanges.size();
     191          49 :     aTokens.reserve(nCount);
     192          81 :     for (size_t i = 0; i < nCount; ++i)
     193             :     {
     194          32 :         const ScRange* pRange = rRanges[i];
     195          32 :         if (!pRange)
     196             :             // failed.
     197          49 :             return;
     198             : 
     199          32 :         ScTokenRef pToken;
     200          32 :         ScRefTokenHelper::getTokenFromRange(pToken,* pRange);
     201          32 :         aTokens.push_back(pToken);
     202          32 :     }
     203          49 :     pTokens.swap(aTokens);
     204             : }
     205             : 
     206        7939 : bool ScRefTokenHelper::isRef(const ScTokenRef& pToken)
     207             : {
     208        7939 :     switch (pToken->GetType())
     209             :     {
     210             :         case svSingleRef:
     211             :         case svDoubleRef:
     212             :         case svExternalSingleRef:
     213             :         case svExternalDoubleRef:
     214        7939 :             return true;
     215             :         default:
     216             :             ;
     217             :     }
     218           0 :     return false;
     219             : }
     220             : 
     221       23204 : bool ScRefTokenHelper::isExternalRef(const ScTokenRef& pToken)
     222             : {
     223       23204 :     switch (pToken->GetType())
     224             :     {
     225             :         case svExternalSingleRef:
     226             :         case svExternalDoubleRef:
     227           0 :             return true;
     228             :         default:
     229             :             ;
     230             :     }
     231       23204 :     return false;
     232             : }
     233             : 
     234           4 : bool ScRefTokenHelper::intersects(
     235             :     const vector<ScTokenRef>& rTokens, const ScTokenRef& pToken, const ScAddress& rPos)
     236             : {
     237           4 :     if (!isRef(pToken))
     238           0 :         return false;
     239             : 
     240           4 :     bool bExternal = isExternalRef(pToken);
     241           4 :     sal_uInt16 nFileId = bExternal ? pToken->GetIndex() : 0;
     242             : 
     243           4 :     ScRange aRange;
     244           4 :     getRangeFromToken(aRange, pToken, rPos, bExternal);
     245             : 
     246           4 :     vector<ScTokenRef>::const_iterator itr = rTokens.begin(), itrEnd = rTokens.end();
     247           6 :     for (; itr != itrEnd; ++itr)
     248             :     {
     249           4 :         const ScTokenRef& p = *itr;
     250           4 :         if (!isRef(p))
     251           0 :             continue;
     252             : 
     253           4 :         if (bExternal != isExternalRef(p))
     254           0 :             continue;
     255             : 
     256           4 :         ScRange aRange2;
     257           4 :         getRangeFromToken(aRange2, p, rPos, bExternal);
     258             : 
     259           4 :         if (bExternal && nFileId != p->GetIndex())
     260             :             // different external file
     261           0 :             continue;
     262             : 
     263           4 :         if (aRange.Intersects(aRange2))
     264           2 :             return true;
     265             :     }
     266           2 :     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        9756 :     void operator() (vector<ScTokenRef>& rTokens, const ScTokenRef& pToken, const ScAddress& rPos)
     282             :     {
     283        9756 :         join(rTokens, pToken, rPos);
     284        9756 :     }
     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        5747 :     static bool overlaps(T nMin1, T nMax1, T nMin2, T nMax2, T& rNewMin, T& rNewMax)
     300             :     {
     301        5747 :         bool bDisjoint1 = (nMin1 > nMax2) && (nMin1 - nMax2 > 1);
     302        5747 :         bool bDisjoint2  = (nMin2 > nMax1) && (nMin2 - nMax1 > 1);
     303        5747 :         if (bDisjoint1 || bDisjoint2)
     304             :             // These two ranges cannot be joined.  Move on.
     305           0 :             return false;
     306             : 
     307        5747 :         T nMin = nMin1 < nMin2 ? nMin1 : nMin2;
     308        5747 :         T nMax = nMax1 > nMax2 ? nMax1 : nMax2;
     309             : 
     310        5747 :         rNewMin = nMin;
     311        5747 :         rNewMax = nMax;
     312             : 
     313        5747 :         return true;
     314             :     }
     315             : 
     316       10505 :     void join(vector<ScTokenRef>& rTokens, const ScTokenRef& pToken, const ScAddress& rPos)
     317             :     {
     318             :         // Normalize the token to a double reference.
     319             :         ScComplexRefData aData;
     320       10505 :         if (!ScRefTokenHelper::getDoubleRefDataFromToken(aData, pToken))
     321        5050 :             return;
     322             : 
     323             :         // Get the information of the new token.
     324       10479 :         bool bExternal = ScRefTokenHelper::isExternalRef(pToken);
     325       10479 :         sal_uInt16 nFileId = bExternal ? pToken->GetIndex() : 0;
     326       10479 :         OUString aTabName = bExternal ? pToken->GetString().getString() : OUString();
     327             : 
     328       10479 :         bool bJoined = false;
     329       10479 :         vector<ScTokenRef>::iterator itr = rTokens.begin(), itrEnd = rTokens.end();
     330       11948 :         for (; itr != itrEnd; ++itr)
     331             :         {
     332        7216 :             ScTokenRef& pOldToken = *itr;
     333             : 
     334        7216 :             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        7216 :             if (bExternal != ScRefTokenHelper::isExternalRef(pOldToken))
     340             :                 // External and internal refs don't mix.
     341           0 :                 continue;
     342             : 
     343        7216 :             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        7216 :             if (!ScRefTokenHelper::getDoubleRefDataFromToken(aOldData, pOldToken))
     356           0 :                 continue;
     357             : 
     358        7216 :             ScRange aOld = aOldData.toAbs(rPos), aNew = aData.toAbs(rPos);
     359             : 
     360        7216 :             if (aNew.aStart.Tab() != aOld.aStart.Tab() || aNew.aEnd.Tab() != aOld.aEnd.Tab())
     361             :                 // Sheet ranges differ.
     362           0 :                 continue;
     363             : 
     364        7216 :             if (aOld.In(aNew))
     365             :                 // This new range is part of an existing range.  Skip it.
     366           0 :                 return;
     367             : 
     368        7216 :             bool bSameRows = (aNew.aStart.Row() == aOld.aStart.Row()) && (aNew.aEnd.Row() == aOld.aEnd.Row());
     369        7216 :             bool bSameCols = (aNew.aStart.Col() == aOld.aStart.Col()) && (aNew.aEnd.Col() == aOld.aEnd.Col());
     370        7216 :             ScComplexRefData aNewData = aOldData;
     371        7216 :             bool bJoinRanges = false;
     372        7216 :             if (bSameRows)
     373             :             {
     374             :                 SCCOL nNewMin, nNewMax;
     375             :                 bJoinRanges = overlaps(
     376       10352 :                     aNew.aStart.Col(), aNew.aEnd.Col(), aOld.aStart.Col(), aOld.aEnd.Col(),
     377       10352 :                     nNewMin, nNewMax);
     378             : 
     379        2588 :                 if (bJoinRanges)
     380             :                 {
     381        2588 :                     aNew.aStart.SetCol(nNewMin);
     382        2588 :                     aNew.aEnd.SetCol(nNewMax);
     383        2588 :                     aNewData.SetRange(aNew, rPos);
     384             :                 }
     385             :             }
     386        4628 :             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        3159 :                     nNewMin, nNewMax);
     392             : 
     393        3159 :                 if (bJoinRanges)
     394             :                 {
     395        3159 :                     aNew.aStart.SetRow(nNewMin);
     396        3159 :                     aNew.aEnd.SetRow(nNewMax);
     397        3159 :                     aNewData.SetRange(aNew, rPos);
     398             :                 }
     399             :             }
     400             : 
     401        7216 :             if (bJoinRanges)
     402             :             {
     403        5747 :                 if (bExternal)
     404           0 :                     pOldToken.reset(new ScExternalDoubleRefToken(nFileId, aTabName, aNewData));
     405             :                 else
     406        5747 :                     pOldToken.reset(new ScDoubleRefToken(aNewData));
     407             : 
     408        5747 :                 bJoined = true;
     409        5747 :                 break;
     410             :             }
     411             :         }
     412             : 
     413       10479 :         if (bJoined)
     414             :         {
     415        5747 :             if (rTokens.size() == 1)
     416             :                 // There is only one left.  No need to do more joining.
     417        4998 :                 return;
     418             : 
     419             :             // Pop the last token from the list, and keep joining recursively.
     420         749 :             ScTokenRef p = rTokens.back();
     421         749 :             rTokens.pop_back();
     422         749 :             join(rTokens, p, rPos);
     423             :         }
     424             :         else
     425        4732 :             rTokens.push_back(pToken);
     426             :     }
     427             : };
     428             : 
     429             : }
     430             : 
     431        9756 : void ScRefTokenHelper::join(vector<ScTokenRef>& rTokens, const ScTokenRef& pToken, const ScAddress& rPos)
     432             : {
     433             :     JoinRefTokenRanges join;
     434        9756 :     join(rTokens, pToken, rPos);
     435        9756 : }
     436             : 
     437       18868 : bool ScRefTokenHelper::getDoubleRefDataFromToken(ScComplexRefData& rData, const ScTokenRef& pToken)
     438             : {
     439       18868 :     switch (pToken->GetType())
     440             :     {
     441             :         case svSingleRef:
     442             :         case svExternalSingleRef:
     443             :         {
     444       10918 :             const ScSingleRefData& r = *pToken->GetSingleRef();
     445       10918 :             rData.Ref1 = r;
     446       10918 :             rData.Ref1.SetFlag3D(true);
     447       10918 :             rData.Ref2 = r;
     448       10918 :             rData.Ref2.SetFlag3D(false); // Don't display sheet name on second reference.
     449             :         }
     450       10918 :         break;
     451             :         case svDoubleRef:
     452             :         case svExternalDoubleRef:
     453        7923 :             rData = *pToken->GetDoubleRef();
     454        7923 :         break;
     455             :         default:
     456             :             // Not a reference token.  Bail out.
     457          27 :             return false;
     458             :     }
     459       18841 :     return true;
     460             : }
     461             : 
     462           2 : ScTokenRef ScRefTokenHelper::createRefToken(const ScAddress& rAddr)
     463             : {
     464             :     ScSingleRefData aRefData;
     465           2 :     aRefData.InitAddress(rAddr);
     466           2 :     ScTokenRef pRef(new ScSingleRefToken(aRefData));
     467           2 :     return pRef;
     468             : }
     469             : 
     470           1 : ScTokenRef ScRefTokenHelper::createRefToken(const ScRange& rRange)
     471             : {
     472             :     ScComplexRefData aRefData;
     473           1 :     aRefData.InitRange(rRange);
     474           1 :     ScTokenRef pRef(new ScDoubleRefToken(aRefData));
     475           1 :     return pRef;
     476         156 : }
     477             : 
     478             : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Generated by: LCOV version 1.11