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
compileRangeRepresentation(vector<ScTokenRef> & rRefTokens,const OUString & rRangeStr,ScDocument * pDoc,const sal_Unicode cSep,FormulaGrammar::Grammar eGrammar,bool bOnly3DRef)36 void ScRefTokenHelper::compileRangeRepresentation(
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;
95 case svExternalSingleRef:
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
getRangeFromToken(ScRange & rRange,const ScTokenRef & pToken,const ScAddress & rPos,bool bExternal)123 bool ScRefTokenHelper::getRangeFromToken(
124 ScRange& rRange, const ScTokenRef& pToken, const ScAddress& rPos, bool bExternal)
125 {
126 StackVar eType = pToken->GetType();
127 switch (pToken->GetType())
128 {
129 case svSingleRef:
130 case svExternalSingleRef:
131 {
132 if ((eType == svExternalSingleRef && !bExternal) ||
133 (eType == svSingleRef && bExternal))
134 return false;
135
136 const ScSingleRefData& rRefData = *pToken->GetSingleRef();
137 rRange.aStart = rRefData.toAbs(rPos);
138 rRange.aEnd = rRange.aStart;
139 return true;
140 }
141 case svDoubleRef:
142 case svExternalDoubleRef:
143 {
144 if ((eType == svExternalDoubleRef && !bExternal) ||
145 (eType == svDoubleRef && bExternal))
146 return false;
147
148 const ScComplexRefData& rRefData = *pToken->GetDoubleRef();
149 rRange = rRefData.toAbs(rPos);
150 return true;
151 }
152 default:
153 ; // do nothing
154 }
155 return false;
156 }
157
getRangeListFromTokens(ScRangeList & rRangeList,const vector<ScTokenRef> & rTokens,const ScAddress & rPos)158 void ScRefTokenHelper::getRangeListFromTokens(
159 ScRangeList& rRangeList, const vector<ScTokenRef>& rTokens, const ScAddress& rPos)
160 {
161 for (const auto& rToken : rTokens)
162 {
163 ScRange aRange;
164 getRangeFromToken(aRange, rToken, rPos);
165 rRangeList.push_back(aRange);
166 }
167 }
168
getTokenFromRange(ScTokenRef & pToken,const ScRange & rRange)169 void ScRefTokenHelper::getTokenFromRange(ScTokenRef& pToken, const ScRange& rRange)
170 {
171 ScComplexRefData aData;
172 aData.InitRange(rRange);
173 aData.Ref1.SetFlag3D(true);
174
175 // Display sheet name on 2nd reference only when the 1st and 2nd refs are on
176 // different sheets.
177 aData.Ref2.SetFlag3D(rRange.aStart.Tab() != rRange.aEnd.Tab());
178
179 pToken.reset(new ScDoubleRefToken(aData));
180 }
181
getTokensFromRangeList(vector<ScTokenRef> & pTokens,const ScRangeList & rRanges)182 void ScRefTokenHelper::getTokensFromRangeList(vector<ScTokenRef>& pTokens, const ScRangeList& rRanges)
183 {
184 vector<ScTokenRef> aTokens;
185 size_t nCount = rRanges.size();
186 aTokens.reserve(nCount);
187 for (size_t i = 0; i < nCount; ++i)
188 {
189 const ScRange & rRange = rRanges[i];
190 ScTokenRef pToken;
191 ScRefTokenHelper::getTokenFromRange(pToken, rRange);
192 aTokens.push_back(pToken);
193 }
194 pTokens.swap(aTokens);
195 }
196
isRef(const ScTokenRef & pToken)197 bool ScRefTokenHelper::isRef(const ScTokenRef& pToken)
198 {
199 switch (pToken->GetType())
200 {
201 case svSingleRef:
202 case svDoubleRef:
203 case svExternalSingleRef:
204 case svExternalDoubleRef:
205 return true;
206 default:
207 ;
208 }
209 return false;
210 }
211
isExternalRef(const ScTokenRef & pToken)212 bool ScRefTokenHelper::isExternalRef(const ScTokenRef& pToken)
213 {
214 switch (pToken->GetType())
215 {
216 case svExternalSingleRef:
217 case svExternalDoubleRef:
218 return true;
219 default:
220 ;
221 }
222 return false;
223 }
224
intersects(const vector<ScTokenRef> & rTokens,const ScTokenRef & pToken,const ScAddress & rPos)225 bool ScRefTokenHelper::intersects(
226 const vector<ScTokenRef>& rTokens, const ScTokenRef& pToken, const ScAddress& rPos)
227 {
228 if (!isRef(pToken))
229 return false;
230
231 bool bExternal = isExternalRef(pToken);
232 sal_uInt16 nFileId = bExternal ? pToken->GetIndex() : 0;
233
234 ScRange aRange;
235 getRangeFromToken(aRange, pToken, rPos, bExternal);
236
237 for (const ScTokenRef& p : rTokens)
238 {
239 if (!isRef(p))
240 continue;
241
242 if (bExternal != isExternalRef(p))
243 continue;
244
245 ScRange aRange2;
246 getRangeFromToken(aRange2, p, rPos, bExternal);
247
248 if (bExternal && nFileId != p->GetIndex())
249 // different external file
250 continue;
251
252 if (aRange.Intersects(aRange2))
253 return true;
254 }
255 return false;
256 }
257
258 namespace {
259
260 class JoinRefTokenRanges
261 {
262 public:
263 /**
264 * Insert a new reference token into the existing list of reference tokens,
265 * but in that process, try to join as many adjacent ranges as possible.
266 *
267 * @param rTokens existing list of reference tokens
268 * @param rToken new token
269 */
operator ()(vector<ScTokenRef> & rTokens,const ScTokenRef & pToken,const ScAddress & rPos)270 void operator() (vector<ScTokenRef>& rTokens, const ScTokenRef& pToken, const ScAddress& rPos)
271 {
272 join(rTokens, pToken, rPos);
273 }
274
275 private:
276
277 /**
278 * Check two 1-dimensional ranges to see if they overlap each other.
279 *
280 * @param nMin1 min value of range 1
281 * @param nMax1 max value of range 1
282 * @param nMin2 min value of range 2
283 * @param nMax2 max value of range 2
284 * @param rNewMin min value of new range in case they overlap
285 * @param rNewMax max value of new range in case they overlap
286 */
287 template<typename T>
overlaps(T nMin1,T nMax1,T nMin2,T nMax2,T & rNewMin,T & rNewMax)288 static bool overlaps(T nMin1, T nMax1, T nMin2, T nMax2, T& rNewMin, T& rNewMax)
289 {
290 bool bDisjoint1 = (nMin1 > nMax2) && (nMin1 - nMax2 > 1);
291 bool bDisjoint2 = (nMin2 > nMax1) && (nMin2 - nMax1 > 1);
292 if (bDisjoint1 || bDisjoint2)
293 // These two ranges cannot be joined. Move on.
294 return false;
295
296 T nMin = std::min(nMin1, nMin2);
297 T nMax = std::max(nMax1, nMax2);
298
299 rNewMin = nMin;
300 rNewMax = nMax;
301
302 return true;
303 }
304
join(vector<ScTokenRef> & rTokens,const ScTokenRef & pToken,const ScAddress & rPos)305 void join(vector<ScTokenRef>& rTokens, const ScTokenRef& pToken, const ScAddress& rPos)
306 {
307 // Normalize the token to a double reference.
308 ScComplexRefData aData;
309 if (!ScRefTokenHelper::getDoubleRefDataFromToken(aData, pToken))
310 return;
311
312 // Get the information of the new token.
313 bool bExternal = ScRefTokenHelper::isExternalRef(pToken);
314 sal_uInt16 nFileId = bExternal ? pToken->GetIndex() : 0;
315 svl::SharedString aTabName = bExternal ? pToken->GetString() : svl::SharedString::getEmptyString();
316
317 bool bJoined = false;
318 for (ScTokenRef& pOldToken : rTokens)
319 {
320 if (!ScRefTokenHelper::isRef(pOldToken))
321 // A non-ref token should not have been added here in the first
322 // place!
323 continue;
324
325 if (bExternal != ScRefTokenHelper::isExternalRef(pOldToken))
326 // External and internal refs don't mix.
327 continue;
328
329 if (bExternal)
330 {
331 if (nFileId != pOldToken->GetIndex())
332 // Different external files.
333 continue;
334
335 if (aTabName != pOldToken->GetString())
336 // Different table names.
337 continue;
338 }
339
340 ScComplexRefData aOldData;
341 if (!ScRefTokenHelper::getDoubleRefDataFromToken(aOldData, pOldToken))
342 continue;
343
344 ScRange aOld = aOldData.toAbs(rPos), aNew = aData.toAbs(rPos);
345
346 if (aNew.aStart.Tab() != aOld.aStart.Tab() || aNew.aEnd.Tab() != aOld.aEnd.Tab())
347 // Sheet ranges differ.
348 continue;
349
350 if (aOld.In(aNew))
351 // This new range is part of an existing range. Skip it.
352 return;
353
354 bool bSameRows = (aNew.aStart.Row() == aOld.aStart.Row()) && (aNew.aEnd.Row() == aOld.aEnd.Row());
355 bool bSameCols = (aNew.aStart.Col() == aOld.aStart.Col()) && (aNew.aEnd.Col() == aOld.aEnd.Col());
356 ScComplexRefData aNewData = aOldData;
357 bool bJoinRanges = false;
358 if (bSameRows)
359 {
360 SCCOL nNewMin, nNewMax;
361 bJoinRanges = overlaps(
362 aNew.aStart.Col(), aNew.aEnd.Col(), aOld.aStart.Col(), aOld.aEnd.Col(),
363 nNewMin, nNewMax);
364
365 if (bJoinRanges)
366 {
367 aNew.aStart.SetCol(nNewMin);
368 aNew.aEnd.SetCol(nNewMax);
369 aNewData.SetRange(aNew, rPos);
370 }
371 }
372 else if (bSameCols)
373 {
374 SCROW nNewMin, nNewMax;
375 bJoinRanges = overlaps(
376 aNew.aStart.Row(), aNew.aEnd.Row(), aOld.aStart.Row(), aOld.aEnd.Row(),
377 nNewMin, nNewMax);
378
379 if (bJoinRanges)
380 {
381 aNew.aStart.SetRow(nNewMin);
382 aNew.aEnd.SetRow(nNewMax);
383 aNewData.SetRange(aNew, rPos);
384 }
385 }
386
387 if (bJoinRanges)
388 {
389 if (bExternal)
390 pOldToken.reset(new ScExternalDoubleRefToken(nFileId, aTabName, aNewData));
391 else
392 pOldToken.reset(new ScDoubleRefToken(aNewData));
393
394 bJoined = true;
395 break;
396 }
397 }
398
399 if (bJoined)
400 {
401 if (rTokens.size() == 1)
402 // There is only one left. No need to do more joining.
403 return;
404
405 // Pop the last token from the list, and keep joining recursively.
406 ScTokenRef p = rTokens.back();
407 rTokens.pop_back();
408 join(rTokens, p, rPos);
409 }
410 else
411 rTokens.push_back(pToken);
412 }
413 };
414
415 }
416
join(vector<ScTokenRef> & rTokens,const ScTokenRef & pToken,const ScAddress & rPos)417 void ScRefTokenHelper::join(vector<ScTokenRef>& rTokens, const ScTokenRef& pToken, const ScAddress& rPos)
418 {
419 JoinRefTokenRanges join;
420 join(rTokens, pToken, rPos);
421 }
422
getDoubleRefDataFromToken(ScComplexRefData & rData,const ScTokenRef & pToken)423 bool ScRefTokenHelper::getDoubleRefDataFromToken(ScComplexRefData& rData, const ScTokenRef& pToken)
424 {
425 switch (pToken->GetType())
426 {
427 case svSingleRef:
428 case svExternalSingleRef:
429 {
430 const ScSingleRefData& r = *pToken->GetSingleRef();
431 rData.Ref1 = r;
432 rData.Ref1.SetFlag3D(true);
433 rData.Ref2 = r;
434 rData.Ref2.SetFlag3D(false); // Don't display sheet name on second reference.
435 }
436 break;
437 case svDoubleRef:
438 case svExternalDoubleRef:
439 rData = *pToken->GetDoubleRef();
440 break;
441 default:
442 // Not a reference token. Bail out.
443 return false;
444 }
445 return true;
446 }
447
createRefToken(const ScAddress & rAddr)448 ScTokenRef ScRefTokenHelper::createRefToken(const ScAddress& rAddr)
449 {
450 ScSingleRefData aRefData;
451 aRefData.InitAddress(rAddr);
452 ScTokenRef pRef(new ScSingleRefToken(aRefData));
453 return pRef;
454 }
455
createRefToken(const ScRange & rRange)456 ScTokenRef ScRefTokenHelper::createRefToken(const ScRange& rRange)
457 {
458 ScComplexRefData aRefData;
459 aRefData.InitRange(rRange);
460 ScTokenRef pRef(new ScDoubleRefToken(aRefData));
461 return pRef;
462 }
463
464 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
465