Line data Source code
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 :
10 : #include <formula/token.hxx>
11 : #include <formula/vectortoken.hxx>
12 :
13 : #include "compiler.hxx"
14 : #include "grouptokenconverter.hxx"
15 :
16 : using namespace formula;
17 :
18 2 : bool ScGroupTokenConverter::isSelfReferenceRelative(const ScAddress& rRefPos, SCROW nRelRow)
19 : {
20 2 : if (rRefPos.Col() != mrPos.Col())
21 2 : return false;
22 :
23 0 : SCROW nLen = mrCell.GetCellGroup()->mnLength;
24 0 : SCROW nEndRow = mrPos.Row() + nLen - 1;
25 :
26 0 : if (nRelRow < 0)
27 : {
28 0 : SCROW nTest = nEndRow;
29 0 : nTest += nRelRow;
30 0 : if (nTest >= mrPos.Row())
31 0 : return true;
32 : }
33 0 : else if (nRelRow > 0)
34 : {
35 0 : SCROW nTest = mrPos.Row(); // top row.
36 0 : nTest += nRelRow;
37 0 : if (nTest <= nEndRow)
38 0 : return true;
39 : }
40 :
41 0 : return false;
42 : }
43 :
44 0 : bool ScGroupTokenConverter::isSelfReferenceAbsolute(const ScAddress& rRefPos)
45 : {
46 0 : if (rRefPos.Col() != mrPos.Col())
47 0 : return false;
48 :
49 0 : SCROW nLen = mrCell.GetCellGroup()->mnLength;
50 0 : SCROW nEndRow = mrPos.Row() + nLen - 1;
51 :
52 0 : if (rRefPos.Row() < mrPos.Row())
53 0 : return false;
54 :
55 0 : if (rRefPos.Row() > nEndRow)
56 0 : return false;
57 :
58 0 : return true;
59 : }
60 :
61 2 : SCROW ScGroupTokenConverter::trimLength(SCTAB nTab, SCCOL nCol1, SCCOL nCol2, SCROW nRow, SCROW nRowLen)
62 : {
63 2 : SCROW nLastRow = nRow + nRowLen - 1; // current last row.
64 2 : nLastRow = mrDoc.GetLastDataRow(nTab, nCol1, nCol2, nLastRow);
65 2 : if (nLastRow < (nRow + nRowLen - 1))
66 : {
67 : // This can end up negative! Was that the original intent, or
68 : // is it accidental? Was it not like that originally but the
69 : // surrounding conditions changed?
70 0 : nRowLen = nLastRow - nRow + 1;
71 : // Anyway, let's assume it doesn't make sense to return a
72 : // negative value here. But should we then return 0 or 1? In
73 : // the "Column is empty" case below, we return 1, why!? And,
74 : // at the callsites there are tests for a zero value returned
75 : // from this function (but not for a negative one).
76 0 : if (nRowLen < 0)
77 0 : nRowLen = 0;
78 : }
79 2 : else if (nLastRow == 0)
80 : // Column is empty.
81 0 : nRowLen = 1;
82 :
83 2 : return nRowLen;
84 : }
85 :
86 1 : ScGroupTokenConverter::ScGroupTokenConverter(ScTokenArray& rGroupTokens, ScDocument& rDoc, ScFormulaCell& rCell, const ScAddress& rPos) :
87 1 : mrGroupTokens(rGroupTokens), mrDoc(rDoc), mrCell(rCell), mrPos(rPos)
88 :
89 : {
90 1 : }
91 :
92 1 : bool ScGroupTokenConverter::convert(ScTokenArray& rCode, std::vector<ScTokenArray*>& rConversionStack)
93 : {
94 : #if 0
95 : { // debug to start with:
96 : ScCompiler aComp( &mrDoc, mrPos, rCode);
97 : aComp.SetGrammar(formula::FormulaGrammar::GRAM_NATIVE_XL_R1C1);
98 : OUStringBuffer aAsString;
99 : aComp.CreateStringFromTokenArray(aAsString);
100 : }
101 : #endif
102 :
103 1 : rCode.Reset();
104 4 : for (const formula::FormulaToken* p = rCode.First(); p; p = rCode.Next())
105 : {
106 : // A reference can be either absolute or relative. If it's absolute,
107 : // convert it to a static value token. If relative, convert it to a
108 : // vector reference token. Note: we only care about relative vs
109 : // absolute reference state for row directions.
110 :
111 3 : SCROW nLen = mrCell.GetCellGroup()->mnLength;
112 3 : switch (p->GetType())
113 : {
114 : case svSingleRef:
115 : {
116 2 : ScSingleRefData aRef = *p->GetSingleRef();
117 2 : ScAddress aRefPos = aRef.toAbs(mrPos);
118 2 : if (aRef.IsRowRel())
119 : {
120 2 : if (isSelfReferenceRelative(aRefPos, aRef.Row()))
121 0 : return false;
122 :
123 : // Trim data array length to actual data range.
124 2 : SCROW nTrimLen = trimLength(aRefPos.Tab(), aRefPos.Col(), aRefPos.Col(), aRefPos.Row(), nLen);
125 : // Fetch double array guarantees that the length of the
126 : // returned array equals or greater than the requested
127 : // length.
128 :
129 2 : formula::VectorRefArray aArray;
130 2 : if (nTrimLen)
131 2 : aArray = mrDoc.FetchVectorRefArray(aRefPos, nTrimLen);
132 :
133 2 : if (!aArray.isValid())
134 0 : return false;
135 :
136 2 : formula::SingleVectorRefToken aTok(aArray, nLen, nTrimLen);
137 2 : mrGroupTokens.AddToken(aTok);
138 : }
139 : else
140 : {
141 : // Absolute row reference.
142 0 : if (isSelfReferenceAbsolute(aRefPos))
143 0 : return false;
144 :
145 0 : formula::FormulaTokenRef pNewToken = mrDoc.ResolveStaticReference(aRefPos);
146 0 : if (!pNewToken)
147 0 : return false;
148 :
149 0 : mrGroupTokens.AddToken(*pNewToken);
150 : }
151 : }
152 2 : break;
153 : case svDoubleRef:
154 : {
155 0 : ScComplexRefData aRef = *p->GetDoubleRef();
156 0 : ScRange aAbs = aRef.toAbs(mrPos);
157 :
158 : // Check for self reference.
159 0 : if (aRef.Ref1.IsRowRel())
160 : {
161 0 : if (isSelfReferenceRelative(aAbs.aStart, aRef.Ref1.Row()))
162 0 : return false;
163 : }
164 0 : else if (isSelfReferenceAbsolute(aAbs.aStart))
165 0 : return false;
166 :
167 0 : if (aRef.Ref2.IsRowRel())
168 : {
169 0 : if (isSelfReferenceRelative(aAbs.aEnd, aRef.Ref2.Row()))
170 0 : return false;
171 : }
172 0 : else if (isSelfReferenceAbsolute(aAbs.aEnd))
173 0 : return false;
174 :
175 : // Row reference is relative.
176 0 : bool bAbsFirst = !aRef.Ref1.IsRowRel();
177 0 : bool bAbsLast = !aRef.Ref2.IsRowRel();
178 0 : ScAddress aRefPos = aAbs.aStart;
179 0 : size_t nCols = aAbs.aEnd.Col() - aAbs.aStart.Col() + 1;
180 0 : std::vector<formula::VectorRefArray> aArrays;
181 0 : aArrays.reserve(nCols);
182 0 : SCROW nRefRowSize = aAbs.aEnd.Row() - aAbs.aStart.Row() + 1;
183 0 : SCROW nArrayLength = nRefRowSize;
184 0 : if (!bAbsLast)
185 : {
186 : // range end position is relative. Extend the array length.
187 0 : SCROW nLastRefRowOffset = aAbs.aEnd.Row() - mrPos.Row();
188 0 : SCROW nLastRefRow = mrPos.Row() + nLen - 1 + nLastRefRowOffset;
189 0 : SCROW nNewLength = nLastRefRow - aAbs.aStart.Row() + 1;
190 0 : if (nNewLength > nArrayLength)
191 0 : nArrayLength = nNewLength;
192 : }
193 :
194 : // Trim trailing empty rows.
195 0 : SCROW nRequestedLength = nArrayLength; // keep the original length.
196 0 : nArrayLength = trimLength(aRefPos.Tab(), aAbs.aStart.Col(), aAbs.aEnd.Col(), aRefPos.Row(), nArrayLength);
197 :
198 0 : for (SCCOL i = aAbs.aStart.Col(); i <= aAbs.aEnd.Col(); ++i)
199 : {
200 0 : aRefPos.SetCol(i);
201 0 : formula::VectorRefArray aArray;
202 0 : if (nArrayLength)
203 0 : aArray = mrDoc.FetchVectorRefArray(aRefPos, nArrayLength);
204 :
205 0 : if (!aArray.isValid())
206 0 : return false;
207 :
208 0 : aArrays.push_back(aArray);
209 : }
210 :
211 0 : formula::DoubleVectorRefToken aTok(aArrays, nRequestedLength, nArrayLength, nRefRowSize, bAbsFirst, bAbsLast);
212 0 : mrGroupTokens.AddToken(aTok);
213 : }
214 0 : break;
215 : case svIndex:
216 : {
217 : // Named range.
218 0 : ScRangeName* pNames = mrDoc.GetRangeName();
219 0 : if (!pNames)
220 : // This should never fail.
221 0 : return false;
222 :
223 0 : ScRangeData* pRange = pNames->findByIndex(p->GetIndex());
224 0 : if (!pRange)
225 : // No named range exists by that index.
226 0 : return false;
227 :
228 0 : ScTokenArray* pNamedTokens = pRange->GetCode();
229 0 : if (!pNamedTokens)
230 : // This named range is empty.
231 0 : return false;
232 :
233 0 : mrGroupTokens.AddOpCode(ocOpen);
234 :
235 0 : if (std::find(rConversionStack.begin(), rConversionStack.end(), pNamedTokens) != rConversionStack.end())
236 : {
237 : SAL_WARN("sc", "loop in recursive ScGroupTokenConverter::convert");
238 0 : return false;
239 : }
240 :
241 0 : rConversionStack.push_back(pNamedTokens);
242 0 : bool bOk = convert(*pNamedTokens, rConversionStack);
243 0 : rConversionStack.pop_back();
244 0 : if (!bOk)
245 0 : return false;
246 :
247 0 : mrGroupTokens.AddOpCode(ocClose);
248 : }
249 0 : break;
250 : default:
251 1 : mrGroupTokens.AddToken(*p);
252 : }
253 : }
254 :
255 1 : return true;
256 156 : }
257 :
258 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|