1 /************************************************************** 2 * 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, 14 * software distributed under the License is distributed on an 15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 * KIND, either express or implied. See the License for the 17 * specific language governing permissions and limitations 18 * under the License. 19 * 20 *************************************************************/ 21 22 23 24 // MARKER(update_precomp.py): autogen include statement, do not remove 25 #include "precompiled_sc.hxx" 26 27 28 #include "reftokenhelper.hxx" 29 #include "document.hxx" 30 #include "rangeutl.hxx" 31 #include "compiler.hxx" 32 #include "tokenarray.hxx" 33 34 #include "rtl/ustring.hxx" 35 #include "formula/grammar.hxx" 36 #include "formula/token.hxx" 37 38 using namespace formula; 39 40 using ::std::vector; 41 using ::std::auto_ptr; 42 using ::rtl::OUString; 43 44 void ScRefTokenHelper::compileRangeRepresentation( 45 vector<ScSharedTokenRef>& rRefTokens, const OUString& rRangeStr, ScDocument* pDoc, FormulaGrammar::Grammar eGrammar) 46 { 47 const sal_Unicode cSep = GetScCompilerNativeSymbol(ocSep).GetChar(0); 48 const sal_Unicode cQuote = '\''; 49 50 // #i107275# ignore parentheses 51 OUString aRangeStr = rRangeStr; 52 while( (aRangeStr.getLength() >= 2) && (aRangeStr[ 0 ] == '(') && (aRangeStr[ aRangeStr.getLength() - 1 ] == ')') ) 53 aRangeStr = aRangeStr.copy( 1, aRangeStr.getLength() - 2 ); 54 55 bool bFailure = false; 56 sal_Int32 nOffset = 0; 57 while (nOffset >= 0 && !bFailure) 58 { 59 OUString aToken; 60 ScRangeStringConverter::GetTokenByOffset(aToken, aRangeStr, nOffset, cSep, cQuote); 61 if (nOffset < 0) 62 break; 63 64 ScCompiler aCompiler(pDoc, ScAddress(0,0,0)); 65 aCompiler.SetGrammar(eGrammar); 66 auto_ptr<ScTokenArray> pArray(aCompiler.CompileString(aToken)); 67 68 // There MUST be exactly one reference per range token and nothing 69 // else, and it MUST be a valid reference, not some #REF! 70 sal_uInt16 nLen = pArray->GetLen(); 71 if (!nLen) 72 continue; // Should a missing range really be allowed? 73 if (nLen != 1) 74 bFailure = true; 75 else 76 { 77 pArray->Reset(); 78 const FormulaToken* p = pArray->GetNextReference(); 79 if (!p) 80 bFailure = true; 81 else 82 { 83 const ScToken* pT = static_cast<const ScToken*>(p); 84 switch (pT->GetType()) 85 { 86 case svSingleRef: 87 if (!pT->GetSingleRef().Valid()) 88 bFailure = true; 89 break; 90 case svDoubleRef: 91 if (!pT->GetDoubleRef().Valid()) 92 bFailure = true; 93 break; 94 case svExternalSingleRef: 95 if (!pT->GetSingleRef().ValidExternal()) 96 bFailure = true; 97 break; 98 case svExternalDoubleRef: 99 if (!pT->GetDoubleRef().ValidExternal()) 100 bFailure = true; 101 break; 102 default: 103 ; 104 } 105 if (!bFailure) 106 rRefTokens.push_back( 107 ScSharedTokenRef(static_cast<ScToken*>(p->Clone()))); 108 } 109 } 110 111 #if 0 112 switch (p->GetType()) 113 { 114 case svSingleRef: 115 fprintf(stdout, "ScChart2DataProvider::compileRangeRepresentation: single ref\n"); 116 break; 117 case svDoubleRef: 118 fprintf(stdout, "ScChart2DataProvider::compileRangeRepresentation: double ref\n"); 119 break; 120 case svExternalSingleRef: 121 fprintf(stdout, "ScChart2DataProvider::compileRangeRepresentation: external single ref\n"); 122 break; 123 case svExternalDoubleRef: 124 fprintf(stdout, "ScChart2DataProvider::compileRangeRepresentation: external double ref\n"); 125 break; 126 default: 127 ; 128 } 129 #endif 130 131 } 132 if (bFailure) 133 rRefTokens.clear(); 134 } 135 136 bool ScRefTokenHelper::getRangeFromToken(ScRange& rRange, const ScSharedTokenRef& pToken, bool bExternal) 137 { 138 StackVar eType = pToken->GetType(); 139 switch (pToken->GetType()) 140 { 141 case svSingleRef: 142 case svExternalSingleRef: 143 { 144 if ((eType == svExternalSingleRef && !bExternal) || 145 (eType == svSingleRef && bExternal)) 146 return false; 147 148 const ScSingleRefData& rRefData = pToken->GetSingleRef(); 149 150 if(!rRefData.Valid()) 151 { 152 OSL_ENSURE(false, "RefData out of range, correct before usage (!)"); 153 } 154 155 rRange.aStart.SetCol(rRefData.nCol); 156 rRange.aStart.SetRow(rRefData.nRow); 157 rRange.aStart.SetTab(rRefData.nTab); 158 rRange.aEnd = rRange.aStart; 159 return true; 160 } 161 case svDoubleRef: 162 case svExternalDoubleRef: 163 { 164 if ((eType == svExternalDoubleRef && !bExternal) || 165 (eType == svDoubleRef && bExternal)) 166 return false; 167 168 const ScComplexRefData& rRefData = pToken->GetDoubleRef(); 169 170 if(!rRefData.Valid()) 171 { 172 OSL_ENSURE(false, "RefData out of range, correct before usage (!)"); 173 } 174 175 rRange.aStart.SetCol(rRefData.Ref1.nCol); 176 rRange.aStart.SetRow(rRefData.Ref1.nRow); 177 rRange.aStart.SetTab(rRefData.Ref1.nTab); 178 rRange.aEnd.SetCol(rRefData.Ref2.nCol); 179 rRange.aEnd.SetRow(rRefData.Ref2.nRow); 180 rRange.aEnd.SetTab(rRefData.Ref2.nTab); 181 return true; 182 } 183 default: 184 ; // do nothing 185 } 186 return false; 187 } 188 189 void ScRefTokenHelper::getRangeListFromTokens(ScRangeList& rRangeList, const vector<ScSharedTokenRef>& rTokens) 190 { 191 vector<ScSharedTokenRef>::const_iterator itr = rTokens.begin(), itrEnd = rTokens.end(); 192 for (; itr != itrEnd; ++itr) 193 { 194 ScRange aRange; 195 getRangeFromToken(aRange, *itr); 196 rRangeList.Append(aRange); 197 } 198 } 199 200 void ScRefTokenHelper::getTokenFromRange(ScSharedTokenRef& pToken, const ScRange& rRange) 201 { 202 ScComplexRefData aData; 203 aData.InitFlags(); 204 aData.Ref1.nCol = rRange.aStart.Col(); 205 aData.Ref1.nRow = rRange.aStart.Row(); 206 aData.Ref1.nTab = rRange.aStart.Tab(); 207 aData.Ref1.SetColRel(false); 208 aData.Ref1.SetRowRel(false); 209 aData.Ref1.SetTabRel(false); 210 aData.Ref1.SetFlag3D(true); 211 212 aData.Ref2.nCol = rRange.aEnd.Col(); 213 aData.Ref2.nRow = rRange.aEnd.Row(); 214 aData.Ref2.nTab = rRange.aEnd.Tab(); 215 aData.Ref2.SetColRel(false); 216 aData.Ref2.SetRowRel(false); 217 aData.Ref2.SetTabRel(false); 218 // Display sheet name on 2nd reference only when the 1st and 2nd refs are on 219 // different sheets. 220 aData.Ref2.SetFlag3D(aData.Ref1.nTab != aData.Ref2.nTab); 221 222 pToken.reset(new ScDoubleRefToken(aData)); 223 } 224 225 void ScRefTokenHelper::getTokensFromRangeList(vector<ScSharedTokenRef>& pTokens, const ScRangeList& rRanges) 226 { 227 vector<ScSharedTokenRef> aTokens; 228 sal_uInt32 nCount = rRanges.Count(); 229 aTokens.reserve(nCount); 230 for (sal_uInt32 i = 0; i < nCount; ++i) 231 { 232 ScRange* pRange = static_cast<ScRange*>(rRanges.GetObject(i)); 233 if (!pRange) 234 // failed. 235 return; 236 237 ScSharedTokenRef pToken; 238 ScRefTokenHelper::getTokenFromRange(pToken,* pRange); 239 aTokens.push_back(pToken); 240 } 241 pTokens.swap(aTokens); 242 } 243 244 bool ScRefTokenHelper::isRef(const ScSharedTokenRef& pToken) 245 { 246 switch (pToken->GetType()) 247 { 248 case svSingleRef: 249 case svDoubleRef: 250 case svExternalSingleRef: 251 case svExternalDoubleRef: 252 return true; 253 default: 254 ; 255 } 256 return false; 257 } 258 259 bool ScRefTokenHelper::isExternalRef(const ScSharedTokenRef& pToken) 260 { 261 switch (pToken->GetType()) 262 { 263 case svExternalSingleRef: 264 case svExternalDoubleRef: 265 return true; 266 default: 267 ; 268 } 269 return false; 270 } 271 272 bool ScRefTokenHelper::intersects(const vector<ScSharedTokenRef>& rTokens, const ScSharedTokenRef& pToken) 273 { 274 if (!isRef(pToken)) 275 return false; 276 277 bool bExternal = isExternalRef(pToken); 278 sal_uInt16 nFileId = bExternal ? pToken->GetIndex() : 0; 279 280 ScRange aRange; 281 getRangeFromToken(aRange, pToken, bExternal); 282 283 vector<ScSharedTokenRef>::const_iterator itr = rTokens.begin(), itrEnd = rTokens.end(); 284 for (; itr != itrEnd; ++itr) 285 { 286 const ScSharedTokenRef& p = *itr; 287 if (!isRef(p)) 288 continue; 289 290 if (bExternal != isExternalRef(p)) 291 continue; 292 293 ScRange aRange2; 294 getRangeFromToken(aRange2, p, bExternal); 295 296 if (bExternal && nFileId != p->GetIndex()) 297 // different external file 298 continue; 299 300 if (aRange.Intersects(aRange2)) 301 return true; 302 } 303 return false; 304 } 305 306 namespace { 307 308 class JoinRefTokenRanges 309 { 310 public: 311 /** 312 * Insert a new reference token into the existing list of reference tokens, 313 * but in that process, try to join as many adjacent ranges as possible. 314 * 315 * @param rTokens existing list of reference tokens 316 * @param rToken new token 317 */ 318 void operator() (vector<ScSharedTokenRef>& rTokens, const ScSharedTokenRef& pToken) 319 { 320 join(rTokens, pToken); 321 } 322 323 private: 324 325 /** 326 * Check two 1-dimensional ranges to see if they overlap each other. 327 * 328 * @param nMin1 min value of range 1 329 * @param nMax1 max value of range 1 330 * @param nMin2 min value of range 2 331 * @param nMax2 max value of range 2 332 * @param rNewMin min value of new range in case they overlap 333 * @param rNewMax max value of new range in case they overlap 334 */ 335 template<typename T> 336 static bool overlaps(T nMin1, T nMax1, T nMin2, T nMax2, T& rNewMin, T& rNewMax) 337 { 338 bool bDisjoint1 = (nMin1 > nMax2) && (nMin1 - nMax2 > 1); 339 bool bDisjoint2 = (nMin2 > nMax1) && (nMin2 - nMax1 > 1); 340 if (bDisjoint1 || bDisjoint2) 341 // These two ranges cannot be joined. Move on. 342 return false; 343 344 T nMin = nMin1 < nMin2 ? nMin1 : nMin2; 345 T nMax = nMax1 > nMax2 ? nMax1 : nMax2; 346 347 rNewMin = nMin; 348 rNewMax = nMax; 349 350 return true; 351 } 352 353 bool isContained(const ScComplexRefData& aOldData, const ScComplexRefData& aData) const 354 { 355 // Check for containment. 356 bool bRowsContained = (aOldData.Ref1.nRow <= aData.Ref1.nRow) && (aData.Ref2.nRow <= aOldData.Ref2.nRow); 357 bool bColsContained = (aOldData.Ref1.nCol <= aData.Ref1.nCol) && (aData.Ref2.nCol <= aOldData.Ref2.nCol); 358 return (bRowsContained && bColsContained); 359 } 360 361 void join(vector<ScSharedTokenRef>& rTokens, const ScSharedTokenRef& pToken) 362 { 363 // Normalize the token to a double reference. 364 ScComplexRefData aData; 365 if (!ScRefTokenHelper::getDoubleRefDataFromToken(aData, pToken)) 366 return; 367 368 // Get the information of the new token. 369 bool bExternal = ScRefTokenHelper::isExternalRef(pToken); 370 sal_uInt16 nFileId = bExternal ? pToken->GetIndex() : 0; 371 String aTabName = bExternal ? pToken->GetString() : String(); 372 373 bool bJoined = false; 374 vector<ScSharedTokenRef>::iterator itr = rTokens.begin(), itrEnd = rTokens.end(); 375 for (; itr != itrEnd; ++itr) 376 { 377 ScSharedTokenRef& pOldToken = *itr; 378 379 if (!ScRefTokenHelper::isRef(pOldToken)) 380 // A non-ref token should not have been added here in the first 381 // place! 382 continue; 383 384 if (bExternal != ScRefTokenHelper::isExternalRef(pOldToken)) 385 // External and internal refs don't mix. 386 continue; 387 388 if (bExternal) 389 { 390 if (nFileId != pOldToken->GetIndex()) 391 // Different external files. 392 continue; 393 394 if (aTabName != pOldToken->GetString()) 395 // Different table names. 396 continue; 397 } 398 399 ScComplexRefData aOldData; 400 if (!ScRefTokenHelper::getDoubleRefDataFromToken(aOldData, pOldToken)) 401 continue; 402 403 if (aData.Ref1.nTab != aOldData.Ref1.nTab || aData.Ref2.nTab != aOldData.Ref2.nTab) 404 // Sheet ranges differ. 405 continue; 406 407 if (isContained(aOldData, aData)) 408 // This new range is part of an existing range. Skip it. 409 return; 410 411 bool bSameRows = (aData.Ref1.nRow == aOldData.Ref1.nRow) && (aData.Ref2.nRow == aOldData.Ref2.nRow); 412 bool bSameCols = (aData.Ref1.nCol == aOldData.Ref1.nCol) && (aData.Ref2.nCol == aOldData.Ref2.nCol); 413 ScComplexRefData aNewData = aOldData; 414 bool bJoinRanges = false; 415 if (bSameRows) 416 { 417 bJoinRanges = overlaps( 418 aData.Ref1.nCol, aData.Ref2.nCol, aOldData.Ref1.nCol, aOldData.Ref2.nCol, 419 aNewData.Ref1.nCol, aNewData.Ref2.nCol); 420 } 421 else if (bSameCols) 422 { 423 bJoinRanges = overlaps( 424 aData.Ref1.nRow, aData.Ref2.nRow, aOldData.Ref1.nRow, aOldData.Ref2.nRow, 425 aNewData.Ref1.nRow, aNewData.Ref2.nRow); 426 } 427 428 if (bJoinRanges) 429 { 430 if (bExternal) 431 pOldToken.reset(new ScExternalDoubleRefToken(nFileId, aTabName, aNewData)); 432 else 433 pOldToken.reset(new ScDoubleRefToken(aNewData)); 434 435 bJoined = true; 436 break; 437 } 438 } 439 440 if (bJoined) 441 { 442 if (rTokens.size() == 1) 443 // There is only one left. No need to do more joining. 444 return; 445 446 // Pop the last token from the list, and keep joining recursively. 447 ScSharedTokenRef p = rTokens.back(); 448 rTokens.pop_back(); 449 join(rTokens, p); 450 } 451 else 452 rTokens.push_back(pToken); 453 } 454 }; 455 456 } 457 458 void ScRefTokenHelper::join(vector<ScSharedTokenRef>& rTokens, const ScSharedTokenRef& pToken) 459 { 460 JoinRefTokenRanges join; 461 join(rTokens, pToken); 462 } 463 464 bool ScRefTokenHelper::getDoubleRefDataFromToken(ScComplexRefData& rData, const ScSharedTokenRef& pToken) 465 { 466 switch (pToken->GetType()) 467 { 468 case svSingleRef: 469 case svExternalSingleRef: 470 { 471 const ScSingleRefData& r = pToken->GetSingleRef(); 472 rData.Ref1 = r; 473 rData.Ref1.SetFlag3D(true); 474 rData.Ref2 = r; 475 rData.Ref2.SetFlag3D(false); // Don't display sheet name on second reference. 476 } 477 break; 478 case svDoubleRef: 479 case svExternalDoubleRef: 480 rData = pToken->GetDoubleRef(); 481 break; 482 default: 483 // Not a reference token. Bail out. 484 return false; 485 } 486 return true; 487 } 488