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
32using namespace formula;
33
34using ::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;
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:
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:
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
190void 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
203void 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:
226 return true;
227 default:
228 ;
229 }
230 return false;
231}
232
234{
235 switch (pToken->GetType())
236 {
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
280namespace {
281
282class JoinRefTokenRanges
283{
284public:
292 void operator() (const ScDocument* pDoc, vector<ScTokenRef>& rTokens, const ScTokenRef& pToken, const ScAddress& rPos)
293 {
294 join(pDoc, rTokens, pToken, rPos);
295 }
296
297private:
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.Contains(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
439void 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:
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:
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: */
SCTAB Tab() const
Definition: address.hxx:283
SCROW Row() const
Definition: address.hxx:274
SCCOL Col() const
Definition: address.hxx:279
ScSheetLimits & GetSheetLimits() const
Definition: document.hxx:898
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
SC_DLLPUBLIC bool IsReference(ScRange &rRef) const
Definition: rangenam.cxx:371
bool HasReferences() const
Definition: rangenam.cxx:520
void push_back(const ScRange &rRange)
Definition: rangelst.cxx:1137
size_t size() const
Definition: rangelst.hxx:89
static void GetTokenByOffset(OUString &rToken, std::u16string_view rString, sal_Int32 &nOffset, sal_Unicode cSeparator=' ', sal_Unicode cQuote='\'')
Definition: rangeutl.cxx:414
ScAddress aEnd
Definition: address.hxx:498
bool Intersects(const ScRange &rRange) const
Definition: address.hxx:734
bool Contains(const ScAddress &) const
is Address& fully in Range?
Definition: address.hxx:718
ScAddress aStart
Definition: address.hxx:497
static const SharedString & getEmptyString()
int nCount
DocumentType eType
void * p
constexpr OUStringLiteral aData
bool SC_DLLPUBLIC isRef(const ScTokenRef &pToken)
void getRangeListFromTokens(const ScDocument *pDoc, ScRangeList &rRangeList, const ::std::vector< ScTokenRef > &pTokens, const ScAddress &rPos)
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.
ScTokenRef createRefToken(const ScDocument &rDoc, const ScAddress &rAddr)
void getTokensFromRangeList(const ScDocument *pDoc, ::std::vector< ScTokenRef > &pTokens, const ScRangeList &rRanges)
bool getRangeFromToken(const ScDocument *pDoc, ScRange &rRange, const ScTokenRef &pToken, const ScAddress &rPos, bool bExternal=false)
bool SC_DLLPUBLIC intersects(const ScDocument *pDoc, const ::std::vector< ScTokenRef > &rTokens, const ScTokenRef &pToken, const ScAddress &rPos)
void getTokenFromRange(const ScDocument *pDoc, ScTokenRef &pToken, const ScRange &rRange)
Create a double reference token from a range object.
bool SC_DLLPUBLIC isExternalRef(const ScTokenRef &pToken)
void SC_DLLPUBLIC join(const ScDocument *pDoc, ::std::vector< ScTokenRef > &rTokens, const ScTokenRef &pToken, const ScAddress &rPos)
bool getDoubleRefDataFromToken(ScComplexRefData &rData, const ScTokenRef &pToken)
StackVar
svIndex
svExternalDoubleRef
svDoubleRef
svExternalSingleRef
svString
svSingleRef
int i
ocName
Complex reference (a range) into the sheet.
Definition: refdata.hxx:123
SC_DLLPUBLIC ScRange toAbs(const ScSheetLimits &rLimits, const ScAddress &rPos) const
Definition: refdata.cxx:493
void InitRange(const ScRange &rRange)
Definition: refdata.hxx:130
bool Valid(const ScDocument &rDoc) const
Definition: refdata.cxx:478
ScSingleRefData Ref2
Definition: refdata.hxx:125
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
ScSingleRefData Ref1
Definition: refdata.hxx:124
Single reference (one address) into the sheet.
Definition: refdata.hxx:30
void InitAddress(const ScAddress &rAdr)
InitAddress: InitFlags and set address.
Definition: refdata.cxx:27
bool Valid(const ScDocument &rDoc) const
Definition: refdata.cxx:130
ScAddress toAbs(const ScSheetLimits &rLimits, const ScAddress &rPos) const
Definition: refdata.cxx:193
void SetFlag3D(bool bVal)
Definition: refdata.hxx:89
bool IsFlag3D() const
Definition: refdata.hxx:90
sal_uInt16 sal_Unicode
sal_Int16 SCCOL
Definition: types.hxx:21
::boost::intrusive_ptr< formula::FormulaToken > ScTokenRef
Definition: types.hxx:29
sal_Int32 SCROW
Definition: types.hxx:17