LibreOffice Module sc (master)  1
reftokenhelper.cxx
Go to the documentation of this file.
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 <memory>
31 
32 using namespace formula;
33 
34 using ::std::vector;
35 
37  vector<ScTokenRef>& rRefTokens, const OUString& rRangeStr, ScDocument& rDoc,
38  const sal_Unicode cSep, FormulaGrammar::Grammar eGrammar, bool bOnly3DRef)
39 {
40  // #i107275# ignore parentheses
41  OUString aRangeStr = rRangeStr;
42  while( (aRangeStr.getLength() >= 2) && (aRangeStr[ 0 ] == '(') && (aRangeStr[ aRangeStr.getLength() - 1 ] == ')') )
43  aRangeStr = aRangeStr.copy( 1, aRangeStr.getLength() - 2 );
44 
45  bool bFailure = false;
46  sal_Int32 nOffset = 0;
47  while (nOffset >= 0 && !bFailure)
48  {
49  OUString aToken;
50  ScRangeStringConverter::GetTokenByOffset(aToken, aRangeStr, nOffset, cSep);
51  if (nOffset < 0)
52  break;
53 
54  ScCompiler aCompiler(rDoc, ScAddress(0,0,0), eGrammar);
55  std::unique_ptr<ScTokenArray> pArray(aCompiler.CompileString(aToken));
56 
57  // There MUST be exactly one reference per range token and nothing
58  // else, and it MUST be a valid reference, not some #REF!
59  sal_uInt16 nLen = pArray->GetLen();
60  if (!nLen)
61  continue; // Should a missing range really be allowed?
62  if (nLen != 1)
63  {
64  bFailure = true;
65  break;
66  }
67 
68  const FormulaToken* p = pArray->FirstToken();
69  if (!p)
70  {
71  bFailure = true;
72  break;
73  }
74 
75  switch (p->GetType())
76  {
77  case svSingleRef:
78  {
79  const ScSingleRefData& rRef = *p->GetSingleRef();
80  if (!rRef.Valid(rDoc))
81  bFailure = true;
82  else if (bOnly3DRef && !rRef.IsFlag3D())
83  bFailure = true;
84  }
85  break;
86  case svDoubleRef:
87  {
88  const ScComplexRefData& rRef = *p->GetDoubleRef();
89  if (!rRef.Valid(rDoc))
90  bFailure = true;
91  else if (bOnly3DRef && !rRef.Ref1.IsFlag3D())
92  bFailure = true;
93  }
94  break;
96  {
97  if (!p->GetSingleRef()->ValidExternal(rDoc))
98  bFailure = true;
99  }
100  break;
101  case svExternalDoubleRef:
102  {
103  if (!p->GetDoubleRef()->ValidExternal(rDoc))
104  bFailure = true;
105  }
106  break;
107  case svString:
108  if (p->GetString().isEmpty())
109  bFailure = true;
110  break;
111  case svIndex:
112  {
113  if (p->GetOpCode() == ocName)
114  {
115  ScRangeData* pNameRange = rDoc.FindRangeNameBySheetAndIndex(p->GetSheet(), p->GetIndex());
116  if (!pNameRange->HasReferences())
117  bFailure = true;
118  }
119  }
120  break;
121  default:
122  bFailure = true;
123  break;
124  }
125  if (!bFailure)
126  rRefTokens.emplace_back(p->Clone());
127 
128  }
129  if (bFailure)
130  rRefTokens.clear();
131 }
132 
134  const ScDocument* pDoc,
135  ScRange& rRange, const ScTokenRef& pToken, const ScAddress& rPos, bool bExternal)
136 {
137  StackVar eType = pToken->GetType();
138  switch (pToken->GetType())
139  {
140  case svSingleRef:
141  case svExternalSingleRef:
142  {
143  if ((eType == svExternalSingleRef && !bExternal) ||
144  (eType == svSingleRef && bExternal))
145  return false;
146 
147  const ScSingleRefData& rRefData = *pToken->GetSingleRef();
148  rRange.aStart = rRefData.toAbs(*pDoc, rPos);
149  rRange.aEnd = rRange.aStart;
150  return true;
151  }
152  case svDoubleRef:
153  case svExternalDoubleRef:
154  {
155  if ((eType == svExternalDoubleRef && !bExternal) ||
156  (eType == svDoubleRef && bExternal))
157  return false;
158 
159  const ScComplexRefData& rRefData = *pToken->GetDoubleRef();
160  rRange = rRefData.toAbs(*pDoc, rPos);
161  return true;
162  }
163  case svIndex:
164  {
165  if (pToken->GetOpCode() == ocName)
166  {
167  ScRangeData* pNameRange = pDoc->FindRangeNameBySheetAndIndex(pToken->GetSheet(), pToken->GetIndex());
168  if (pNameRange->IsReference(rRange, rPos))
169  return true;
170  }
171  return false;
172  }
173  default:
174  ; // do nothing
175  }
176  return false;
177 }
178 
180  const ScDocument* pDoc, ScRangeList& rRangeList, const vector<ScTokenRef>& rTokens, const ScAddress& rPos)
181 {
182  for (const auto& rToken : rTokens)
183  {
184  ScRange aRange;
185  getRangeFromToken(pDoc, aRange, rToken, rPos);
186  rRangeList.push_back(aRange);
187  }
188 }
189 
190 void ScRefTokenHelper::getTokenFromRange(const ScDocument* pDoc, ScTokenRef& pToken, const ScRange& rRange)
191 {
193  aData.InitRange(rRange);
194  aData.Ref1.SetFlag3D(true);
195 
196  // Display sheet name on 2nd reference only when the 1st and 2nd refs are on
197  // different sheets.
198  aData.Ref2.SetFlag3D(rRange.aStart.Tab() != rRange.aEnd.Tab());
199 
200  pToken.reset(new ScDoubleRefToken(pDoc->GetSheetLimits(), aData));
201 }
202 
203 void ScRefTokenHelper::getTokensFromRangeList(const ScDocument* pDoc, vector<ScTokenRef>& pTokens, const ScRangeList& rRanges)
204 {
205  vector<ScTokenRef> aTokens;
206  size_t nCount = rRanges.size();
207  aTokens.reserve(nCount);
208  for (size_t i = 0; i < nCount; ++i)
209  {
210  const ScRange & rRange = rRanges[i];
211  ScTokenRef pToken;
212  ScRefTokenHelper::getTokenFromRange(pDoc, pToken, rRange);
213  aTokens.push_back(pToken);
214  }
215  pTokens.swap(aTokens);
216 }
217 
219 {
220  switch (pToken->GetType())
221  {
222  case svSingleRef:
223  case svDoubleRef:
224  case svExternalSingleRef:
225  case svExternalDoubleRef:
226  return true;
227  default:
228  ;
229  }
230  return false;
231 }
232 
234 {
235  switch (pToken->GetType())
236  {
237  case svExternalSingleRef:
238  case svExternalDoubleRef:
239  return true;
240  default:
241  ;
242  }
243  return false;
244 }
245 
247  const ScDocument* pDoc,
248  const vector<ScTokenRef>& rTokens, const ScTokenRef& pToken, const ScAddress& rPos)
249 {
250  if (!isRef(pToken))
251  return false;
252 
253  bool bExternal = isExternalRef(pToken);
254  sal_uInt16 nFileId = bExternal ? pToken->GetIndex() : 0;
255 
256  ScRange aRange;
257  getRangeFromToken(pDoc, aRange, pToken, rPos, bExternal);
258 
259  for (const ScTokenRef& p : rTokens)
260  {
261  if (!isRef(p))
262  continue;
263 
264  if (bExternal != isExternalRef(p))
265  continue;
266 
267  ScRange aRange2;
268  getRangeFromToken(pDoc, aRange2, p, rPos, bExternal);
269 
270  if (bExternal && nFileId != p->GetIndex())
271  // different external file
272  continue;
273 
274  if (aRange.Intersects(aRange2))
275  return true;
276  }
277  return false;
278 }
279 
280 namespace {
281 
282 class JoinRefTokenRanges
283 {
284 public:
292  void operator() (const ScDocument* pDoc, vector<ScTokenRef>& rTokens, const ScTokenRef& pToken, const ScAddress& rPos)
293  {
294  join(pDoc, rTokens, pToken, rPos);
295  }
296 
297 private:
298 
309  template<typename T>
310  static bool overlaps(T nMin1, T nMax1, T nMin2, T nMax2, T& rNewMin, T& rNewMax)
311  {
312  bool bDisjoint1 = (nMin1 > nMax2) && (nMin1 - nMax2 > 1);
313  bool bDisjoint2 = (nMin2 > nMax1) && (nMin2 - nMax1 > 1);
314  if (bDisjoint1 || bDisjoint2)
315  // These two ranges cannot be joined. Move on.
316  return false;
317 
318  T nMin = std::min(nMin1, nMin2);
319  T nMax = std::max(nMax1, nMax2);
320 
321  rNewMin = nMin;
322  rNewMax = nMax;
323 
324  return true;
325  }
326 
327  void join(const ScDocument* pDoc, vector<ScTokenRef>& rTokens, const ScTokenRef& pToken, const ScAddress& rPos)
328  {
329  // Normalize the token to a double reference.
332  return;
333 
334  // Get the information of the new token.
335  bool bExternal = ScRefTokenHelper::isExternalRef(pToken);
336  sal_uInt16 nFileId = bExternal ? pToken->GetIndex() : 0;
337  svl::SharedString aTabName = bExternal ? pToken->GetString() : svl::SharedString::getEmptyString();
338 
339  bool bJoined = false;
340  for (ScTokenRef& pOldToken : rTokens)
341  {
342  if (!ScRefTokenHelper::isRef(pOldToken))
343  // A non-ref token should not have been added here in the first
344  // place!
345  continue;
346 
347  if (bExternal != ScRefTokenHelper::isExternalRef(pOldToken))
348  // External and internal refs don't mix.
349  continue;
350 
351  if (bExternal)
352  {
353  if (nFileId != pOldToken->GetIndex())
354  // Different external files.
355  continue;
356 
357  if (aTabName != pOldToken->GetString())
358  // Different table names.
359  continue;
360  }
361 
362  ScComplexRefData aOldData;
363  if (!ScRefTokenHelper::getDoubleRefDataFromToken(aOldData, pOldToken))
364  continue;
365 
366  ScRange aOld = aOldData.toAbs(*pDoc, rPos), aNew = aData.toAbs(*pDoc, rPos);
367 
368  if (aNew.aStart.Tab() != aOld.aStart.Tab() || aNew.aEnd.Tab() != aOld.aEnd.Tab())
369  // Sheet ranges differ.
370  continue;
371 
372  if (aOld.In(aNew))
373  // This new range is part of an existing range. Skip it.
374  return;
375 
376  bool bSameRows = (aNew.aStart.Row() == aOld.aStart.Row()) && (aNew.aEnd.Row() == aOld.aEnd.Row());
377  bool bSameCols = (aNew.aStart.Col() == aOld.aStart.Col()) && (aNew.aEnd.Col() == aOld.aEnd.Col());
378  ScComplexRefData aNewData = aOldData;
379  bool bJoinRanges = false;
380  if (bSameRows)
381  {
382  SCCOL nNewMin, nNewMax;
383  bJoinRanges = overlaps(
384  aNew.aStart.Col(), aNew.aEnd.Col(), aOld.aStart.Col(), aOld.aEnd.Col(),
385  nNewMin, nNewMax);
386 
387  if (bJoinRanges)
388  {
389  aNew.aStart.SetCol(nNewMin);
390  aNew.aEnd.SetCol(nNewMax);
391  aNewData.SetRange(pDoc->GetSheetLimits(), aNew, rPos);
392  }
393  }
394  else if (bSameCols)
395  {
396  SCROW nNewMin, nNewMax;
397  bJoinRanges = overlaps(
398  aNew.aStart.Row(), aNew.aEnd.Row(), aOld.aStart.Row(), aOld.aEnd.Row(),
399  nNewMin, nNewMax);
400 
401  if (bJoinRanges)
402  {
403  aNew.aStart.SetRow(nNewMin);
404  aNew.aEnd.SetRow(nNewMax);
405  aNewData.SetRange(pDoc->GetSheetLimits(), aNew, rPos);
406  }
407  }
408 
409  if (bJoinRanges)
410  {
411  if (bExternal)
412  pOldToken.reset(new ScExternalDoubleRefToken(nFileId, aTabName, aNewData));
413  else
414  pOldToken.reset(new ScDoubleRefToken(pDoc->GetSheetLimits(), aNewData));
415 
416  bJoined = true;
417  break;
418  }
419  }
420 
421  if (bJoined)
422  {
423  if (rTokens.size() == 1)
424  // There is only one left. No need to do more joining.
425  return;
426 
427  // Pop the last token from the list, and keep joining recursively.
428  ScTokenRef p = rTokens.back();
429  rTokens.pop_back();
430  join(pDoc, rTokens, p, rPos);
431  }
432  else
433  rTokens.push_back(pToken);
434  }
435 };
436 
437 }
438 
439 void ScRefTokenHelper::join(const ScDocument* pDoc, vector<ScTokenRef>& rTokens, const ScTokenRef& pToken, const ScAddress& rPos)
440 {
441  JoinRefTokenRanges join;
442  join(pDoc, rTokens, pToken, rPos);
443 }
444 
446 {
447  switch (pToken->GetType())
448  {
449  case svSingleRef:
450  case svExternalSingleRef:
451  {
452  const ScSingleRefData& r = *pToken->GetSingleRef();
453  rData.Ref1 = r;
454  rData.Ref1.SetFlag3D(true);
455  rData.Ref2 = r;
456  rData.Ref2.SetFlag3D(false); // Don't display sheet name on second reference.
457  }
458  break;
459  case svDoubleRef:
460  case svExternalDoubleRef:
461  rData = *pToken->GetDoubleRef();
462  break;
463  default:
464  // Not a reference token. Bail out.
465  return false;
466  }
467  return true;
468 }
469 
471 {
472  ScSingleRefData aRefData;
473  aRefData.InitAddress(rAddr);
474  ScTokenRef pRef(new ScSingleRefToken(rDoc.GetSheetLimits(), aRefData));
475  return pRef;
476 }
477 
479 {
480  ScComplexRefData aRefData;
481  aRefData.InitRange(rRange);
482  ScTokenRef pRef(new ScDoubleRefToken(rDoc.GetSheetLimits(), aRefData));
483  return pRef;
484 }
485 
486 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
StackVar
void compileRangeRepresentation(::std::vector< ScTokenRef > &rRefTokens, const OUString &rRangeStr, ScDocument &rDoc, const sal_Unicode cSep,::formula::FormulaGrammar::Grammar eGrammar, bool bOnly3DRef=false)
Compile an array of reference tokens from a data source range string.
ScAddress aStart
Definition: address.hxx:499
ocName
bool SC_DLLPUBLIC isExternalRef(const ScTokenRef &pToken)
SCROW Row() const
Definition: address.hxx:261
void getTokenFromRange(const ScDocument *pDoc, ScTokenRef &pToken, const ScRange &rRange)
Create a double reference token from a range object.
SC_DLLPUBLIC ScRange toAbs(const ScSheetLimits &rLimits, const ScAddress &rPos) const
Definition: refdata.cxx:493
bool Valid(const ScDocument &rDoc) const
Definition: refdata.cxx:130
Single reference (one address) into the sheet.
Definition: refdata.hxx:29
ScAddress toAbs(const ScSheetLimits &rLimits, const ScAddress &rPos) const
Definition: refdata.cxx:193
bool Intersects(const ScRange &rRange) const
Definition: address.cxx:1558
void getRangeListFromTokens(const ScDocument *pDoc, ScRangeList &rRangeList, const ::std::vector< ScTokenRef > &pTokens, const ScAddress &rPos)
void SC_DLLPUBLIC join(const ScDocument *pDoc,::std::vector< ScTokenRef > &rTokens, const ScTokenRef &pToken, const ScAddress &rPos)
ScAddress aEnd
Definition: address.hxx:500
OpCode GetOpCode() const
static const SharedString & getEmptyString()
bool SC_DLLPUBLIC isRef(const ScTokenRef &pToken)
virtual sal_Int16 GetSheet() const
sal_uInt16 sal_Unicode
int nCount
virtual const ScSingleRefData * GetSingleRef() const
void push_back(const ScRange &rRange)
Definition: rangelst.cxx:1141
ScTokenRef createRefToken(const ScDocument &rDoc, const ScAddress &rAddr)
SCTAB Tab() const
Definition: address.hxx:270
bool HasReferences() const
Definition: rangenam.cxx:503
constexpr OUStringLiteral aData
DocumentType eType
virtual const ScComplexRefData * GetDoubleRef() const
bool ValidExternal(const ScDocument &rDoc) const
In external references nTab is -1 if the external document was not loaded but the sheet was cached...
Definition: refdata.cxx:183
int i
bool isEmpty() const
void SetFlag3D(bool bVal)
Definition: refdata.hxx:89
void InitAddress(const ScAddress &rAdr)
InitAddress: InitFlags and set address.
Definition: refdata.cxx:27
sal_Int16 SCCOL
Definition: types.hxx:21
bool getDoubleRefDataFromToken(ScComplexRefData &rData, const ScTokenRef &pToken)
ScSheetLimits & GetSheetLimits() const
Definition: document.hxx:873
ScSingleRefData Ref1
Definition: refdata.hxx:124
size_t size() const
Definition: rangelst.hxx:89
ScSingleRefData Ref2
Definition: refdata.hxx:125
svExternalDoubleRef
svExternalSingleRef
bool In(const ScAddress &) const
is Address& in Range?
Definition: address.hxx:732
virtual FormulaToken * Clone() const
virtual sal_uInt16 GetIndex() const
bool IsFlag3D() const
Definition: refdata.hxx:90
SCCOL Col() const
Definition: address.hxx:266
sal_Int32 SCROW
Definition: types.hxx:17
bool Valid(const ScDocument &rDoc) const
Definition: refdata.cxx:478
virtual const svl::SharedString & GetString() const
void InitRange(const ScRange &rRange)
Definition: refdata.hxx:130
::boost::intrusive_ptr< formula::FormulaToken > ScTokenRef
Definition: types.hxx:29
void SetRange(const ScSheetLimits &rLimits, const ScRange &rRange, const ScAddress &rPos)
Set a new range, assuming that the ordering of the range matches the ordering of the reference data f...
Definition: refdata.cxx:498
bool ValidExternal(const ScDocument &rDoc) const
In external references nTab is -1 for the start tab and -1 for the end tab if one sheet and the exter...
Definition: refdata.cxx:483
void * p
SC_DLLPUBLIC bool IsReference(ScRange &rRef) const
Definition: rangenam.cxx:368
Complex reference (a range) into the sheet.
Definition: refdata.hxx:122
bool SC_DLLPUBLIC intersects(const ScDocument *pDoc, const ::std::vector< ScTokenRef > &rTokens, const ScTokenRef &pToken, const ScAddress &rPos)
bool getRangeFromToken(const ScDocument *pDoc, ScRange &rRange, const ScTokenRef &pToken, const ScAddress &rPos, bool bExternal=false)
ScRangeData * FindRangeNameBySheetAndIndex(SCTAB nTab, sal_uInt16 nIndex) const
Find a named expression / range name in either global or a local scope.
Definition: documen3.cxx:269
static void GetTokenByOffset(OUString &rToken, const OUString &rString, sal_Int32 &nOffset, sal_Unicode cSeparator= ' ', sal_Unicode cQuote= '\'')
Definition: rangeutl.cxx:406
StackVar GetType() const
void getTokensFromRangeList(const ScDocument *pDoc,::std::vector< ScTokenRef > &pTokens, const ScRangeList &rRanges)