LibreOffice Module sc (master)  1
reffind.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 <sal/config.h>
21 
23 
24 #include <reffind.hxx>
25 #include <global.hxx>
26 #include <compiler.hxx>
27 #include <document.hxx>
28 
29 namespace {
30 
31 // Include colon; addresses in range reference are handled individually.
32 const sal_Unicode pDelimiters[] = {
33  '=','(',')','+','-','*','/','^','&',' ','{','}','<','>',':', 0
34 };
35 
36 bool IsText( sal_Unicode c )
37 {
38  bool bFound = ScGlobal::UnicodeStrChr( pDelimiters, c );
39  if (bFound)
40  // This is one of delimiters, therefore not text.
41  return false;
42 
43  // argument separator is configurable.
45  return c != sep;
46 }
47 
48 bool IsText( bool& bQuote, sal_Unicode c )
49 {
50  if (c == '\'')
51  {
52  bQuote = !bQuote;
53  return true;
54  }
55  if (bQuote)
56  return true;
57 
58  return IsText(c);
59 }
60 
66 sal_Int32 FindStartPos(const sal_Unicode* p, sal_Int32 nStartPos, sal_Int32 nEndPos)
67 {
68  while (nStartPos <= nEndPos && !IsText(p[nStartPos]))
69  ++nStartPos;
70 
71  return nStartPos;
72 }
73 
74 sal_Int32 FindEndPosA1(const sal_Unicode* p, sal_Int32 nStartPos, sal_Int32 nEndPos)
75 {
76  bool bQuote = false;
77  sal_Int32 nNewEnd = nStartPos;
78  while (nNewEnd <= nEndPos && IsText(bQuote, p[nNewEnd]))
79  ++nNewEnd;
80 
81  return nNewEnd;
82 }
83 
84 sal_Int32 FindEndPosR1C1(const sal_Unicode* p, sal_Int32 nStartPos, sal_Int32 nEndPos)
85 {
86  sal_Int32 nNewEnd = nStartPos;
87  p = &p[nStartPos];
88  for (; nNewEnd <= nEndPos; ++p, ++nNewEnd)
89  {
90  if (*p == '\'')
91  {
92  // Skip until the closing quote.
93  for (; nNewEnd <= nEndPos; ++p, ++nNewEnd)
94  if (*p == '\'')
95  break;
96  if (nNewEnd > nEndPos)
97  break;
98  }
99  else if (*p == '[')
100  {
101  // Skip until the closing braket.
102  for (; nNewEnd <= nEndPos; ++p, ++nNewEnd)
103  if (*p == ']')
104  break;
105  if (nNewEnd > nEndPos)
106  break;
107  }
108  else if (!IsText(*p))
109  break;
110  }
111 
112  return nNewEnd;
113 }
114 
119 sal_Int32 FindEndPos(const sal_Unicode* p, sal_Int32 nStartPos, sal_Int32 nEndPos,
121 {
122  switch (eConv)
123  {
125  return FindEndPosR1C1(p, nStartPos, nEndPos);
128  default:
129  return FindEndPosA1(p, nStartPos, nEndPos);
130  }
131 }
132 
133 void ExpandToTextA1(const sal_Unicode* p, sal_Int32 nLen, sal_Int32& rStartPos, sal_Int32& rEndPos)
134 {
135  bool bQuote = false; // skip quoted text
136  while (rStartPos > 0 && IsText(bQuote, p[rStartPos - 1]) )
137  --rStartPos;
138  if (rEndPos)
139  --rEndPos;
140  while (rEndPos+1 < nLen && IsText(p[rEndPos + 1]) )
141  ++rEndPos;
142 }
143 
144 void ExpandToTextR1C1(const sal_Unicode* p, sal_Int32 nLen, sal_Int32& rStartPos, sal_Int32& rEndPos)
145 {
146  // move back the start position to the first text character.
147  if (rStartPos > 0)
148  {
149  for (--rStartPos; rStartPos > 0; --rStartPos)
150  {
151  sal_Unicode c = p[rStartPos];
152  if (c == '\'')
153  {
154  // Skip until the opening quote.
155  for (--rStartPos; rStartPos > 0; --rStartPos)
156  {
157  c = p[rStartPos];
158  if (c == '\'')
159  break;
160  }
161  if (rStartPos == 0)
162  break;
163  }
164  else if (c == ']')
165  {
166  // Skip until the opening braket.
167  for (--rStartPos; rStartPos > 0; --rStartPos)
168  {
169  c = p[rStartPos];
170  if (c == '[')
171  break;
172  }
173  if (rStartPos == 0)
174  break;
175  }
176  else if (!IsText(c))
177  {
178  ++rStartPos;
179  break;
180  }
181  }
182  }
183 
184  // move forward the end position to the last text character.
185  rEndPos = FindEndPosR1C1(p, rEndPos, nLen-1);
186 }
187 
188 void ExpandToText(const sal_Unicode* p, sal_Int32 nLen, sal_Int32& rStartPos, sal_Int32& rEndPos,
190 {
191  switch (eConv)
192  {
194  ExpandToTextR1C1(p, nLen, rStartPos, rEndPos);
195  break;
198  default:
199  ExpandToTextA1(p, nLen, rStartPos, rEndPos);
200  }
201 }
202 
203 }
204 
206  const OUString& rFormula, const ScAddress& rPos,
208  maFormula(rFormula),
209  meConv(eConvP),
210  mrDoc(rDoc),
211  maPos(rPos),
212  mnFound(0),
213  mnSelStart(0),
214  mnSelEnd(0)
215 {
216 }
217 
219 {
220 }
221 
223 {
225  ScRefFlags nNew = nOld & Mask_ABS;
226  nNew = ScRefFlags( o3tl::underlyingEnumValue(nNew) - 1 ) & Mask_ABS; // weiterzaehlen
227 
228  if (!(nOld & ScRefFlags::TAB_3D))
229  nNew &= ~ScRefFlags::TAB_ABS; // not 3D -> never absolute!
230 
231  return (nOld & ~Mask_ABS) | nNew;
232 }
233 
234 void ScRefFinder::ToggleRel( sal_Int32 nStartPos, sal_Int32 nEndPos )
235 {
236  sal_Int32 nLen = maFormula.getLength();
237  if (nLen <= 0)
238  return;
239  const sal_Unicode* pSource = maFormula.getStr(); // for quick access
240 
241  // expand selection, and instead of selection start- and end-index
242 
243  if ( nEndPos < nStartPos )
244  ::std::swap(nEndPos, nStartPos);
245 
246  ExpandToText(pSource, nLen, nStartPos, nEndPos, meConv);
247 
248  OUStringBuffer aResult;
249  OUString aExpr;
250  OUString aSep;
251  ScAddress aAddr;
252  mnFound = 0;
253 
254  sal_Int32 nLoopStart = nStartPos;
255  while ( nLoopStart <= nEndPos )
256  {
257  // Determine the start and end positions of a text segment. Note that
258  // the end position returned from FindEndPos may be one position after
259  // the last character position in case of the last segment.
260  sal_Int32 nEStart = FindStartPos(pSource, nLoopStart, nEndPos);
261  sal_Int32 nEEnd = FindEndPos(pSource, nEStart, nEndPos, meConv);
262 
263  aSep = maFormula.copy(nLoopStart, nEStart-nLoopStart);
264  if (nEEnd < maFormula.getLength())
265  aExpr = maFormula.copy(nEStart, nEEnd-nEStart);
266  else
267  aExpr = maFormula.copy(nEStart);
268 
269  // Check the validity of the expression, and toggle the relative flag.
270  ScAddress::Details aDetails(meConv, maPos.Row(), maPos.Col());
271  ScAddress::ExternalInfo aExtInfo;
272  ScRefFlags nResult = aAddr.Parse(aExpr, mrDoc, aDetails, &aExtInfo);
273  if ( nResult & ScRefFlags::VALID )
274  {
275  ScRefFlags nFlags;
276  if( aExtInfo.mbExternal )
277  { // retain external doc name and tab name before toggle relative flag
278  sal_Int32 nSep;
279  switch(meConv)
280  {
284  nSep = aExpr.lastIndexOf('!');
285  break;
287  default:
288  nSep = aExpr.lastIndexOf('.');
289  break;
290  }
291  if (nSep >= 0)
292  {
293  OUString aRef = aExpr.copy(nSep+1);
294  OUString aExtDocNameTabName = aExpr.copy(0, nSep+1);
295  nResult = aAddr.Parse(aRef, mrDoc, aDetails);
296  aAddr.SetTab(0); // force to first tab to avoid error on checking
297  nFlags = lcl_NextFlags( nResult );
298  aExpr = aExtDocNameTabName + aAddr.Format(nFlags, &mrDoc, aDetails);
299  }
300  else
301  {
302  assert(!"Invalid syntax according to address convention.");
303  }
304  }
305  else
306  {
307  nFlags = lcl_NextFlags( nResult );
308  aExpr = aAddr.Format(nFlags, &mrDoc, aDetails);
309  }
310 
311  sal_Int32 nAbsStart = nStartPos+aResult.getLength()+aSep.getLength();
312 
313  if (!mnFound) // first reference ?
314  mnSelStart = nAbsStart;
315  mnSelEnd = nAbsStart + aExpr.getLength(); // selection, no indices
316  ++mnFound;
317  }
318 
319  // assemble
320 
321  aResult.append(aSep);
322  aResult.append(aExpr);
323 
324  nLoopStart = nEEnd;
325  }
326 
327  OUString aTotal = maFormula.subView(0, nStartPos) + aResult.makeStringAndClear();
328  if (nEndPos < maFormula.getLength()-1)
329  aTotal += maFormula.subView(nEndPos+1);
330 
331  maFormula = aTotal;
332 }
333 
334 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
SC_DLLPUBLIC void Format(OStringBuffer &r, ScRefFlags nFlags, const ScDocument *pDocument=nullptr, const Details &rDetails=detailsOOOa1) const
Definition: address.cxx:2116
formula::FormulaGrammar::AddressConvention meConv
Definition: reffind.hxx:29
SCROW Row() const
Definition: address.hxx:261
sal_Int32 mnFound
Definition: reffind.hxx:32
constexpr std::underlying_type_t< T > underlyingEnumValue(T e)
void ToggleRel(sal_Int32 nStartPos, sal_Int32 nEndPos)
Definition: reffind.cxx:234
sal_Int32 mnSelEnd
Definition: reffind.hxx:34
sal_uInt16 sal_Unicode
static ScRefFlags lcl_NextFlags(ScRefFlags nOld)
Definition: reffind.cxx:222
static sal_Unicode GetNativeSymbolChar(OpCode eOp)
void SetTab(SCTAB nTabP)
Definition: address.hxx:282
OUString maFormula
ScRefFinder(const OUString &rFormula, const ScAddress &rPos, ScDocument &rDoc, formula::FormulaGrammar::AddressConvention eConvP=formula::FormulaGrammar::CONV_OOO)
Definition: reffind.cxx:205
SCCOL Col() const
Definition: address.hxx:266
ScAddress maPos
Definition: reffind.hxx:31
sal_Int32 mnSelStart
Definition: reffind.hxx:33
OUString maFormula
Definition: reffind.hxx:28
ScDocument & mrDoc
Definition: reffind.hxx:30
static const sal_Unicode * UnicodeStrChr(const sal_Unicode *pStr, sal_Unicode c)
strchr() functionality on unicode, as long as we need it for FormulaToken etc.
Definition: global.cxx:643
SC_DLLPUBLIC ScRefFlags Parse(const OUString &, const ScDocument &, const Details &rDetails=detailsOOOa1, ExternalInfo *pExtInfo=nullptr, const css::uno::Sequence< css::sheet::ExternalLinkInfo > *pExternalLinks=nullptr, sal_Int32 *pSheetEndPos=nullptr, const OUString *pErrRef=nullptr)
Definition: address.cxx:1548
ScRefFlags
Definition: address.hxx:144