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* pDoc,
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(pDoc, 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(pDoc))
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(pDoc))
90  bFailure = true;
91  else if (bOnly3DRef && !rRef.Ref1.IsFlag3D())
92  bFailure = true;
93  }
94  break;
96  {
97  if (!p->GetSingleRef()->ValidExternal(pDoc))
98  bFailure = true;
99  }
100  break;
101  case svExternalDoubleRef:
102  {
103  if (!p->GetDoubleRef()->ValidExternal(pDoc))
104  bFailure = true;
105  }
106  break;
107  case svString:
108  if (p->GetString().isEmpty())
109  bFailure = true;
110  break;
111  default:
112  bFailure = true;
113  break;
114  }
115  if (!bFailure)
116  rRefTokens.emplace_back(p->Clone());
117 
118  }
119  if (bFailure)
120  rRefTokens.clear();
121 }
122 
124  const ScDocument* pDoc,
125  ScRange& rRange, const ScTokenRef& pToken, const ScAddress& rPos, bool bExternal)
126 {
127  StackVar eType = pToken->GetType();
128  switch (pToken->GetType())
129  {
130  case svSingleRef:
131  case svExternalSingleRef:
132  {
133  if ((eType == svExternalSingleRef && !bExternal) ||
134  (eType == svSingleRef && bExternal))
135  return false;
136 
137  const ScSingleRefData& rRefData = *pToken->GetSingleRef();
138  rRange.aStart = rRefData.toAbs(pDoc, rPos);
139  rRange.aEnd = rRange.aStart;
140  return true;
141  }
142  case svDoubleRef:
143  case svExternalDoubleRef:
144  {
145  if ((eType == svExternalDoubleRef && !bExternal) ||
146  (eType == svDoubleRef && bExternal))
147  return false;
148 
149  const ScComplexRefData& rRefData = *pToken->GetDoubleRef();
150  rRange = rRefData.toAbs(pDoc, rPos);
151  return true;
152  }
153  default:
154  ; // do nothing
155  }
156  return false;
157 }
158 
160  const ScDocument* pDoc, ScRangeList& rRangeList, const vector<ScTokenRef>& rTokens, const ScAddress& rPos)
161 {
162  for (const auto& rToken : rTokens)
163  {
164  ScRange aRange;
165  getRangeFromToken(pDoc, aRange, rToken, rPos);
166  rRangeList.push_back(aRange);
167  }
168 }
169 
170 void ScRefTokenHelper::getTokenFromRange(const ScDocument* pDoc, ScTokenRef& pToken, const ScRange& rRange)
171 {
173  aData.InitRange(rRange);
174  aData.Ref1.SetFlag3D(true);
175 
176  // Display sheet name on 2nd reference only when the 1st and 2nd refs are on
177  // different sheets.
178  aData.Ref2.SetFlag3D(rRange.aStart.Tab() != rRange.aEnd.Tab());
179 
180  pToken.reset(new ScDoubleRefToken(pDoc->GetSheetLimits(), aData));
181 }
182 
183 void ScRefTokenHelper::getTokensFromRangeList(const ScDocument* pDoc, vector<ScTokenRef>& pTokens, const ScRangeList& rRanges)
184 {
185  vector<ScTokenRef> aTokens;
186  size_t nCount = rRanges.size();
187  aTokens.reserve(nCount);
188  for (size_t i = 0; i < nCount; ++i)
189  {
190  const ScRange & rRange = rRanges[i];
191  ScTokenRef pToken;
192  ScRefTokenHelper::getTokenFromRange(pDoc, pToken, rRange);
193  aTokens.push_back(pToken);
194  }
195  pTokens.swap(aTokens);
196 }
197 
199 {
200  switch (pToken->GetType())
201  {
202  case svSingleRef:
203  case svDoubleRef:
204  case svExternalSingleRef:
205  case svExternalDoubleRef:
206  return true;
207  default:
208  ;
209  }
210  return false;
211 }
212 
214 {
215  switch (pToken->GetType())
216  {
217  case svExternalSingleRef:
218  case svExternalDoubleRef:
219  return true;
220  default:
221  ;
222  }
223  return false;
224 }
225 
227  const ScDocument* pDoc,
228  const vector<ScTokenRef>& rTokens, const ScTokenRef& pToken, const ScAddress& rPos)
229 {
230  if (!isRef(pToken))
231  return false;
232 
233  bool bExternal = isExternalRef(pToken);
234  sal_uInt16 nFileId = bExternal ? pToken->GetIndex() : 0;
235 
236  ScRange aRange;
237  getRangeFromToken(pDoc, aRange, pToken, rPos, bExternal);
238 
239  for (const ScTokenRef& p : rTokens)
240  {
241  if (!isRef(p))
242  continue;
243 
244  if (bExternal != isExternalRef(p))
245  continue;
246 
247  ScRange aRange2;
248  getRangeFromToken(pDoc, aRange2, p, rPos, bExternal);
249 
250  if (bExternal && nFileId != p->GetIndex())
251  // different external file
252  continue;
253 
254  if (aRange.Intersects(aRange2))
255  return true;
256  }
257  return false;
258 }
259 
260 namespace {
261 
262 class JoinRefTokenRanges
263 {
264 public:
272  void operator() (const ScDocument* pDoc, vector<ScTokenRef>& rTokens, const ScTokenRef& pToken, const ScAddress& rPos)
273  {
274  join(pDoc, rTokens, pToken, rPos);
275  }
276 
277 private:
278 
289  template<typename T>
290  static bool overlaps(T nMin1, T nMax1, T nMin2, T nMax2, T& rNewMin, T& rNewMax)
291  {
292  bool bDisjoint1 = (nMin1 > nMax2) && (nMin1 - nMax2 > 1);
293  bool bDisjoint2 = (nMin2 > nMax1) && (nMin2 - nMax1 > 1);
294  if (bDisjoint1 || bDisjoint2)
295  // These two ranges cannot be joined. Move on.
296  return false;
297 
298  T nMin = std::min(nMin1, nMin2);
299  T nMax = std::max(nMax1, nMax2);
300 
301  rNewMin = nMin;
302  rNewMax = nMax;
303 
304  return true;
305  }
306 
307  void join(const ScDocument* pDoc, vector<ScTokenRef>& rTokens, const ScTokenRef& pToken, const ScAddress& rPos)
308  {
309  // Normalize the token to a double reference.
312  return;
313 
314  // Get the information of the new token.
315  bool bExternal = ScRefTokenHelper::isExternalRef(pToken);
316  sal_uInt16 nFileId = bExternal ? pToken->GetIndex() : 0;
317  svl::SharedString aTabName = bExternal ? pToken->GetString() : svl::SharedString::getEmptyString();
318 
319  bool bJoined = false;
320  for (ScTokenRef& pOldToken : rTokens)
321  {
322  if (!ScRefTokenHelper::isRef(pOldToken))
323  // A non-ref token should not have been added here in the first
324  // place!
325  continue;
326 
327  if (bExternal != ScRefTokenHelper::isExternalRef(pOldToken))
328  // External and internal refs don't mix.
329  continue;
330 
331  if (bExternal)
332  {
333  if (nFileId != pOldToken->GetIndex())
334  // Different external files.
335  continue;
336 
337  if (aTabName != pOldToken->GetString())
338  // Different table names.
339  continue;
340  }
341 
342  ScComplexRefData aOldData;
343  if (!ScRefTokenHelper::getDoubleRefDataFromToken(aOldData, pOldToken))
344  continue;
345 
346  ScRange aOld = aOldData.toAbs(pDoc, rPos), aNew = aData.toAbs(pDoc, rPos);
347 
348  if (aNew.aStart.Tab() != aOld.aStart.Tab() || aNew.aEnd.Tab() != aOld.aEnd.Tab())
349  // Sheet ranges differ.
350  continue;
351 
352  if (aOld.In(aNew))
353  // This new range is part of an existing range. Skip it.
354  return;
355 
356  bool bSameRows = (aNew.aStart.Row() == aOld.aStart.Row()) && (aNew.aEnd.Row() == aOld.aEnd.Row());
357  bool bSameCols = (aNew.aStart.Col() == aOld.aStart.Col()) && (aNew.aEnd.Col() == aOld.aEnd.Col());
358  ScComplexRefData aNewData = aOldData;
359  bool bJoinRanges = false;
360  if (bSameRows)
361  {
362  SCCOL nNewMin, nNewMax;
363  bJoinRanges = overlaps(
364  aNew.aStart.Col(), aNew.aEnd.Col(), aOld.aStart.Col(), aOld.aEnd.Col(),
365  nNewMin, nNewMax);
366 
367  if (bJoinRanges)
368  {
369  aNew.aStart.SetCol(nNewMin);
370  aNew.aEnd.SetCol(nNewMax);
371  aNewData.SetRange(pDoc->GetSheetLimits(), aNew, rPos);
372  }
373  }
374  else if (bSameCols)
375  {
376  SCROW nNewMin, nNewMax;
377  bJoinRanges = overlaps(
378  aNew.aStart.Row(), aNew.aEnd.Row(), aOld.aStart.Row(), aOld.aEnd.Row(),
379  nNewMin, nNewMax);
380 
381  if (bJoinRanges)
382  {
383  aNew.aStart.SetRow(nNewMin);
384  aNew.aEnd.SetRow(nNewMax);
385  aNewData.SetRange(pDoc->GetSheetLimits(), aNew, rPos);
386  }
387  }
388 
389  if (bJoinRanges)
390  {
391  if (bExternal)
392  pOldToken.reset(new ScExternalDoubleRefToken(nFileId, aTabName, aNewData));
393  else
394  pOldToken.reset(new ScDoubleRefToken(pDoc->GetSheetLimits(), aNewData));
395 
396  bJoined = true;
397  break;
398  }
399  }
400 
401  if (bJoined)
402  {
403  if (rTokens.size() == 1)
404  // There is only one left. No need to do more joining.
405  return;
406 
407  // Pop the last token from the list, and keep joining recursively.
408  ScTokenRef p = rTokens.back();
409  rTokens.pop_back();
410  join(pDoc, rTokens, p, rPos);
411  }
412  else
413  rTokens.push_back(pToken);
414  }
415 };
416 
417 }
418 
419 void ScRefTokenHelper::join(const ScDocument* pDoc, vector<ScTokenRef>& rTokens, const ScTokenRef& pToken, const ScAddress& rPos)
420 {
421  JoinRefTokenRanges join;
422  join(pDoc, rTokens, pToken, rPos);
423 }
424 
426 {
427  switch (pToken->GetType())
428  {
429  case svSingleRef:
430  case svExternalSingleRef:
431  {
432  const ScSingleRefData& r = *pToken->GetSingleRef();
433  rData.Ref1 = r;
434  rData.Ref1.SetFlag3D(true);
435  rData.Ref2 = r;
436  rData.Ref2.SetFlag3D(false); // Don't display sheet name on second reference.
437  }
438  break;
439  case svDoubleRef:
440  case svExternalDoubleRef:
441  rData = *pToken->GetDoubleRef();
442  break;
443  default:
444  // Not a reference token. Bail out.
445  return false;
446  }
447  return true;
448 }
449 
451 {
452  ScSingleRefData aRefData;
453  aRefData.InitAddress(rAddr);
454  ScTokenRef pRef(new ScSingleRefToken(pDoc->GetSheetLimits(), aRefData));
455  return pRef;
456 }
457 
459 {
460  ScComplexRefData aRefData;
461  aRefData.InitRange(rRange);
462  ScTokenRef pRef(new ScDoubleRefToken(pDoc->GetSheetLimits(), aRefData));
463  return pRef;
464 }
465 
466 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
StackVar
bool ValidExternal(const ScDocument *pDoc) const
In external references nTab is -1 if the external document was not loaded but the sheet was cached...
Definition: refdata.cxx:183
virtual svl::SharedString GetString() const
ScAddress aStart
Definition: address.hxx:500
bool SC_DLLPUBLIC isExternalRef(const ScTokenRef &pToken)
SCROW Row() const
Definition: address.hxx:262
void getTokenFromRange(const ScDocument *pDoc, ScTokenRef &pToken, const ScRange &rRange)
Create a double reference token from a range object.
static SharedString getEmptyString()
const char aData[]
Single reference (one address) into the sheet.
Definition: refdata.hxx:30
SC_DLLPUBLIC bool Intersects(const ScRange &rRange) const
Definition: address.cxx:1553
void getRangeListFromTokens(const ScDocument *pDoc, ScRangeList &rRangeList, const ::std::vector< ScTokenRef > &pTokens, const ScAddress &rPos)
void SetRange(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 *pDoc) 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
SC_DLLPUBLIC ScRange toAbs(ScSheetLimits &rLimits, const ScAddress &rPos) const
Definition: refdata.cxx:493
void SC_DLLPUBLIC join(const ScDocument *pDoc,::std::vector< ScTokenRef > &rTokens, const ScTokenRef &pToken, const ScAddress &rPos)
ScAddress aEnd
Definition: address.hxx:501
bool SC_DLLPUBLIC isRef(const ScTokenRef &pToken)
sal_uInt16 sal_Unicode
int nCount
virtual const ScSingleRefData * GetSingleRef() const
void push_back(const ScRange &rRange)
Definition: rangelst.cxx:1144
SCTAB Tab() const
Definition: address.hxx:271
DocumentType eType
virtual const ScComplexRefData * GetDoubleRef() const
int i
bool isEmpty() const
void SetFlag3D(bool bVal)
Definition: refdata.hxx:90
void InitAddress(const ScAddress &rAdr)
InitAddress: InitFlags and set address.
Definition: refdata.cxx:27
sal_Int16 SCCOL
Definition: types.hxx:22
bool getDoubleRefDataFromToken(ScComplexRefData &rData, const ScTokenRef &pToken)
ScSheetLimits & GetSheetLimits() const
Definition: document.hxx:877
ScSingleRefData Ref1
Definition: refdata.hxx:125
size_t size() const
Definition: rangelst.hxx:90
bool Valid(const ScDocument *pDoc) const
Definition: refdata.cxx:478
ScSingleRefData Ref2
Definition: refdata.hxx:126
svExternalDoubleRef
svExternalSingleRef
bool In(const ScAddress &) const
is Address& in Range?
Definition: address.hxx:733
virtual FormulaToken * Clone() const
ScTokenRef createRefToken(const ScDocument *pDoc, const ScAddress &rAddr)
virtual sal_uInt16 GetIndex() const
bool IsFlag3D() const
Definition: refdata.hxx:91
SCCOL Col() const
Definition: address.hxx:267
void compileRangeRepresentation(::std::vector< ScTokenRef > &rRefTokens, const OUString &rRangeStr, ScDocument *pDoc, const sal_Unicode cSep,::formula::FormulaGrammar::Grammar eGrammar, bool bOnly3DRef=false)
Compile an array of reference tokens from a data source range string.
ScAddress toAbs(ScSheetLimits &rLimits, const ScAddress &rPos) const
Definition: refdata.cxx:193
sal_Int32 SCROW
Definition: types.hxx:18
void InitRange(const ScRange &rRange)
Definition: refdata.hxx:130
::boost::intrusive_ptr< formula::FormulaToken > ScTokenRef
Definition: types.hxx:30
void * p
Complex reference (a range) into the sheet.
Definition: refdata.hxx:123
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)
static void GetTokenByOffset(OUString &rToken, const OUString &rString, sal_Int32 &nOffset, sal_Unicode cSeparator= ' ', sal_Unicode cQuote= '\'')
Definition: rangeutl.cxx:390
bool Valid(const ScDocument *pDoc) const
Definition: refdata.cxx:130
StackVar GetType() const
void getTokensFromRangeList(const ScDocument *pDoc,::std::vector< ScTokenRef > &pTokens, const ScRangeList &rRanges)