1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <addressconverter.hxx>
22 #include <com/sun/star/container/XIndexAccess.hpp>
23 #include <com/sun/star/sheet/XCellRangeAddressable.hpp>
24 #include <com/sun/star/sheet/XSpreadsheetDocument.hpp>
25 #include <convuno.hxx>
26 #include <osl/diagnose.h>
27 #include <oox/core/filterbase.hxx>
28 #include <oox/helper/binaryinputstream.hxx>
33 using namespace ::com::sun::star::container
;
34 using namespace ::com::sun::star::sheet
;
35 using namespace ::com::sun::star::table
;
36 using namespace ::com::sun::star::uno
;
40 //! TODO: this limit may change, is there a way to obtain it via API?
41 const sal_Int16 API_MAXTAB
= MAXTAB
;
43 const sal_Int32 OOX_MAXCOL
= static_cast< sal_Int32
>( (1 << 14) - 1 );
44 const sal_Int32 OOX_MAXROW
= static_cast< sal_Int32
>( (1 << 20) - 1 );
45 const sal_Int16 OOX_MAXTAB
= static_cast< sal_Int16
>( (1 << 15) - 1 );
50 void BinAddress::read( SequenceInputStream
& rStrm
)
52 mnRow
= rStrm
.readInt32();
53 mnCol
= rStrm
.readInt32();
56 void BinRange::read( SequenceInputStream
& rStrm
)
58 maFirst
.mnRow
= rStrm
.readInt32();
59 maLast
.mnRow
= rStrm
.readInt32();
60 maFirst
.mnCol
= rStrm
.readInt32();
61 maLast
.mnCol
= rStrm
.readInt32();
64 void BinRangeList::read( SequenceInputStream
& rStrm
)
66 sal_Int32 nCount
= rStrm
.readInt32();
67 mvRanges
.resize( getLimitedValue
< size_t, sal_Int64
>( nCount
, 0, rStrm
.getRemaining() / 16 ) );
68 for( auto& rRange
: mvRanges
)
72 AddressConverter::AddressConverter( const WorkbookHelper
& rHelper
) :
73 WorkbookHelper( rHelper
),
74 mbColOverflow( false ),
75 mbRowOverflow( false ),
76 mbTabOverflow( false )
78 initializeMaxPos( OOX_MAXTAB
, OOX_MAXCOL
, OOX_MAXROW
);
81 bool AddressConverter::parseOoxAddress2d(
82 sal_Int32
& ornColumn
, sal_Int32
& ornRow
,
83 const OUString
& rString
, sal_Int32 nStart
, sal_Int32 nLength
)
85 ornColumn
= ornRow
= 0;
86 if( (nStart
< 0) || (nStart
>= rString
.getLength()) || (nLength
< 2) )
89 const sal_Unicode
* pcChar
= rString
.getStr() + nStart
;
90 const sal_Unicode
* pcEndChar
= pcChar
+ ::std::min( nLength
, rString
.getLength() - nStart
);
92 enum { STATE_COL
, STATE_ROW
} eState
= STATE_COL
;
93 while( pcChar
< pcEndChar
)
95 sal_Unicode cChar
= *pcChar
;
100 if( ('a' <= cChar
) && (cChar
<= 'z') )
101 cChar
= (cChar
- 'a') + 'A';
102 if( ('A' <= cChar
) && (cChar
<= 'Z') )
104 /* Return, if 1-based column index is already 6 characters
105 long (12356631 is column index for column AAAAAA). */
106 if( ornColumn
>= 12356631 )
108 ornColumn
= (ornColumn
* 26) + (cChar
- 'A' + 1);
110 else if( ornColumn
> 0 )
122 if( ('0' <= cChar
) && (cChar
<= '9') )
124 // return, if 1-based row is already 9 digits long
125 if( ornRow
>= 100000000 )
127 ornRow
= (ornRow
* 10) + (cChar
- '0');
139 return (ornColumn
>= 0) && (ornRow
>= 0);
142 bool AddressConverter::parseOoxAddress2d( sal_Int32
& ornColumn
, sal_Int32
& ornRow
, const char* pStr
)
144 ornColumn
= ornRow
= 0;
146 enum { STATE_COL
, STATE_ROW
} eState
= STATE_COL
;
155 if( ('a' <= cChar
) && (cChar
<= 'z') )
156 cChar
= (cChar
- 'a') + 'A';
157 if( ('A' <= cChar
) && (cChar
<= 'Z') )
159 /* Return, if 1-based column index is already 6 characters
160 long (12356631 is column index for column AAAAAA). */
161 if( ornColumn
>= 12356631 )
163 ornColumn
= (ornColumn
* 26) + (cChar
- 'A' + 1);
165 else if( ornColumn
> 0 )
177 if( ('0' <= cChar
) && (cChar
<= '9') )
179 // return, if 1-based row is already 9 digits long
180 if( ornRow
>= 100000000 )
182 ornRow
= (ornRow
* 10) + (cChar
- '0');
194 return (ornColumn
>= 0) && (ornRow
>= 0);
197 bool AddressConverter::parseOoxRange2d(
198 sal_Int32
& ornStartColumn
, sal_Int32
& ornStartRow
,
199 sal_Int32
& ornEndColumn
, sal_Int32
& ornEndRow
,
200 const OUString
& rString
, sal_Int32 nStart
)
202 ornStartColumn
= ornStartRow
= ornEndColumn
= ornEndRow
= 0;
203 if( (nStart
< 0) || (nStart
>= rString
.getLength()) )
206 sal_Int32 nEnd
= nStart
+ ( rString
.getLength() - nStart
);
207 sal_Int32 nColonPos
= rString
.indexOf( ':', nStart
);
208 if( (nStart
< nColonPos
) && (nColonPos
+ 1 < nEnd
) )
211 parseOoxAddress2d( ornStartColumn
, ornStartRow
, rString
, nStart
, nColonPos
- nStart
) &&
212 parseOoxAddress2d( ornEndColumn
, ornEndRow
, rString
, nColonPos
+ 1, SAL_MAX_INT32
- nColonPos
- 1 );
215 if( parseOoxAddress2d( ornStartColumn
, ornStartRow
, rString
, nStart
) )
217 ornEndColumn
= ornStartColumn
;
218 ornEndRow
= ornStartRow
;
225 bool AddressConverter::checkCol( sal_Int32 nCol
, bool bTrackOverflow
)
227 bool bValid
= (0 <= nCol
) && ( nCol
<= maMaxPos
.Col() );
228 if( !bValid
&& bTrackOverflow
)
229 mbColOverflow
= true;
233 bool AddressConverter::checkRow( sal_Int32 nRow
, bool bTrackOverflow
)
235 bool bValid
= (0 <= nRow
) && ( nRow
<= maMaxPos
.Row() );
236 if( !bValid
&& bTrackOverflow
)
237 mbRowOverflow
= true;
241 bool AddressConverter::checkTab( sal_Int16 nSheet
, bool bTrackOverflow
)
243 bool bValid
= (0 <= nSheet
) && ( nSheet
<= maMaxPos
.Tab() );
244 if( !bValid
&& bTrackOverflow
)
245 mbTabOverflow
|= ( nSheet
> maMaxPos
.Tab() ); // do not warn for deleted refs (-1)
249 bool AddressConverter::checkCellAddress( const ScAddress
& rAddress
, bool bTrackOverflow
)
252 checkTab( rAddress
.Tab(), bTrackOverflow
) &&
253 checkCol( rAddress
.Col(), bTrackOverflow
) &&
254 checkRow( rAddress
.Row(), bTrackOverflow
);
257 bool AddressConverter::convertToCellAddressUnchecked( ScAddress
& orAddress
,
258 const OUString
& rString
, sal_Int16 nSheet
)
260 orAddress
.SetTab(nSheet
);
263 bool bRes
= parseOoxAddress2d( nCol
, nRow
, rString
);
264 orAddress
.SetRow(nRow
);
265 orAddress
.SetCol(nCol
);
270 bool AddressConverter::convertToCellAddressUnchecked(
271 ScAddress
& orAddress
, const char* pStr
, sal_Int16 nSheet
)
273 orAddress
.SetTab(nSheet
);
276 bool bRes
= parseOoxAddress2d(nCol
, nRow
, pStr
);
277 orAddress
.SetRow(nRow
);
278 orAddress
.SetCol(nCol
);
283 bool AddressConverter::convertToCellAddress( ScAddress
& orAddress
,
284 const OUString
& rString
, sal_Int16 nSheet
, bool bTrackOverflow
)
287 convertToCellAddressUnchecked( orAddress
, rString
, nSheet
) &&
288 checkCellAddress( orAddress
, bTrackOverflow
);
291 bool AddressConverter::convertToCellAddress(
293 const char* pStr
, sal_Int16 nSheet
, bool bTrackOverflow
)
295 if (!convertToCellAddressUnchecked(rAddress
, pStr
, nSheet
))
298 return checkCellAddress(rAddress
, bTrackOverflow
);
301 ScAddress
AddressConverter::createValidCellAddress(
302 const OUString
& rString
, sal_Int16 nSheet
, bool bTrackOverflow
)
304 ScAddress
aAddress( 0, 0, 0 );
305 if( !convertToCellAddress( aAddress
, rString
, nSheet
, bTrackOverflow
) )
307 aAddress
.SetTab( getLimitedValue
< sal_Int16
, sal_Int16
>( nSheet
, 0, maMaxPos
.Tab() ) );
308 aAddress
.SetCol( ::std::min( aAddress
.Col(), maMaxPos
.Col() ) );
309 aAddress
.SetRow( ::std::min( aAddress
.Row(), maMaxPos
.Row() ) );
314 void AddressConverter::convertToCellAddressUnchecked( ScAddress
& orAddress
,
315 const BinAddress
& rBinAddress
, sal_Int16 nSheet
)
317 orAddress
.SetTab(nSheet
);
318 orAddress
.SetCol(rBinAddress
.mnCol
);
319 orAddress
.SetRow(rBinAddress
.mnRow
);
322 bool AddressConverter::convertToCellAddress( ScAddress
& orAddress
,
323 const BinAddress
& rBinAddress
, sal_Int16 nSheet
, bool bTrackOverflow
)
325 convertToCellAddressUnchecked( orAddress
, rBinAddress
, nSheet
);
326 return checkCellAddress( orAddress
, bTrackOverflow
);
329 ScAddress
AddressConverter::createValidCellAddress(
330 const BinAddress
& rBinAddress
, sal_Int16 nSheet
, bool bTrackOverflow
)
332 ScAddress
aAddress ( 0, 0, 0 );
333 if( !convertToCellAddress( aAddress
, rBinAddress
, nSheet
, bTrackOverflow
) )
335 aAddress
.SetTab( getLimitedValue
< sal_Int16
, sal_Int16
>( nSheet
, 0, maMaxPos
.Tab() ) );
336 aAddress
.SetCol( getLimitedValue
< sal_Int32
, sal_Int32
>( rBinAddress
.mnCol
, 0, sal_Int32( maMaxPos
.Col() ) ) );
337 aAddress
.SetRow( getLimitedValue
< sal_Int32
, sal_Int32
>( rBinAddress
.mnRow
, 0, sal_Int32( maMaxPos
.Row() ) ) );
342 bool AddressConverter::checkCellRange( const ScRange
& rRange
, bool bAllowOverflow
, bool bTrackOverflow
)
345 (checkCol( rRange
.aEnd
.Col(), bTrackOverflow
) || bAllowOverflow
) && // bAllowOverflow after checkCol to track overflow!
346 (checkRow( rRange
.aEnd
.Row(), bTrackOverflow
) || bAllowOverflow
) && // bAllowOverflow after checkRow to track overflow!
347 checkTab( rRange
.aStart
.Tab(), bTrackOverflow
) &&
348 checkCol( rRange
.aStart
.Col(), bTrackOverflow
) &&
349 checkRow( rRange
.aStart
.Row(), bTrackOverflow
);
352 bool AddressConverter::validateCellRange( ScRange
& orRange
, bool bAllowOverflow
, bool bTrackOverflow
)
354 if( orRange
.aStart
.Col() > orRange
.aEnd
.Col() )
356 SCCOL nCol
= orRange
.aStart
.Col();
357 orRange
.aStart
.SetCol( orRange
.aEnd
.Col() );
358 orRange
.aEnd
.SetCol( nCol
);
360 if( orRange
.aStart
.Row() > orRange
.aEnd
.Row() )
362 SCROW nRow
= orRange
.aStart
.Row();
363 orRange
.aStart
.SetRow( orRange
.aEnd
.Row() );
364 orRange
.aEnd
.SetRow( nRow
);
366 if( !checkCellRange( orRange
, bAllowOverflow
, bTrackOverflow
) )
368 if( orRange
.aEnd
.Col() > maMaxPos
.Col() )
369 orRange
.aEnd
.SetCol( maMaxPos
.Col() );
370 if( orRange
.aEnd
.Row() > maMaxPos
.Row() )
371 orRange
.aEnd
.SetRow( maMaxPos
.Row() );
375 bool AddressConverter::convertToCellRangeUnchecked( ScRange
& orRange
,
376 const OUString
& rString
, sal_Int16 nSheet
)
378 orRange
.aStart
.SetTab( nSheet
);
379 orRange
.aEnd
.SetTab( nSheet
);
380 sal_Int32 aStartCol
= orRange
.aStart
.Col();
381 sal_Int32 aStartRow
= orRange
.aStart
.Row();
382 sal_Int32 aEndCol
= orRange
.aEnd
.Col();
383 sal_Int32 aEndRow
= orRange
.aEnd
.Row();
384 bool bReturnValue
= parseOoxRange2d( aStartCol
, aStartRow
, aEndCol
, aEndRow
, rString
);
385 orRange
.aStart
.SetCol( aStartCol
);
386 orRange
.aStart
.SetRow( aStartRow
);
387 orRange
.aEnd
.SetCol( aEndCol
);
388 orRange
.aEnd
.SetRow( aEndRow
);
392 bool AddressConverter::convertToCellRange( ScRange
& orRange
,
393 const OUString
& rString
, sal_Int16 nSheet
, bool bAllowOverflow
, bool bTrackOverflow
)
396 convertToCellRangeUnchecked( orRange
, rString
, nSheet
) &&
397 validateCellRange( orRange
, bAllowOverflow
, bTrackOverflow
);
400 void AddressConverter::convertToCellRangeUnchecked( ScRange
& orRange
,
401 const BinRange
& rBinRange
, sal_Int16 nSheet
)
403 orRange
.aStart
.SetTab( nSheet
);
404 orRange
.aStart
.SetCol( rBinRange
.maFirst
.mnCol
);
405 orRange
.aStart
.SetRow( rBinRange
.maFirst
.mnRow
);
406 orRange
.aEnd
.SetTab( nSheet
);
407 orRange
.aEnd
.SetCol( rBinRange
.maLast
.mnCol
);
408 orRange
.aEnd
.SetRow( rBinRange
.maLast
.mnRow
);
411 bool AddressConverter::convertToCellRange( ScRange
& orRange
,
412 const BinRange
& rBinRange
, sal_Int16 nSheet
, bool bAllowOverflow
, bool bTrackOverflow
)
414 convertToCellRangeUnchecked( orRange
, rBinRange
, nSheet
);
415 return validateCellRange( orRange
, bAllowOverflow
, bTrackOverflow
);
418 void AddressConverter::validateCellRangeList( ScRangeList
& orRanges
, bool bTrackOverflow
)
420 for( size_t nIndex
= orRanges
.size(); nIndex
> 0; --nIndex
)
421 if( !validateCellRange( orRanges
[ nIndex
- 1 ], true, bTrackOverflow
) )
422 orRanges
.Remove( nIndex
- 1 );
425 void AddressConverter::convertToCellRangeList( ScRangeList
& orRanges
,
426 const OUString
& rString
, sal_Int16 nSheet
, bool bTrackOverflow
)
429 sal_Int32 nLen
= rString
.getLength();
431 while( (0 <= nPos
) && (nPos
< nLen
) )
433 OUString aToken
= rString
.getToken( 0, ' ', nPos
);
434 if( !aToken
.isEmpty() && convertToCellRange( aRange
, aToken
, nSheet
, true, bTrackOverflow
) )
435 orRanges
.push_back(aRange
);
439 void AddressConverter::convertToCellRangeList( ScRangeList
& orRanges
,
440 const BinRangeList
& rBinRanges
, sal_Int16 nSheet
, bool bTrackOverflow
)
443 for( const auto& rBinRange
: rBinRanges
)
444 if( convertToCellRange( aRange
, rBinRange
, nSheet
, true, bTrackOverflow
) )
445 orRanges
.push_back( aRange
);
448 Sequence
<CellRangeAddress
> AddressConverter::toApiSequence(const ScRangeList
& orRanges
)
450 const size_t nSize
= orRanges
.size();
451 Sequence
<CellRangeAddress
> aRangeSequence(nSize
);
452 CellRangeAddress
* pApiRanges
= aRangeSequence
.getArray();
453 for (size_t i
= 0; i
< nSize
; ++i
)
455 ScUnoConversion::FillApiRange(pApiRanges
[i
], orRanges
[i
]);
457 return aRangeSequence
;
460 // private --------------------------------------------------------------------
462 void AddressConverter::initializeMaxPos(
463 sal_Int16 nMaxXlsTab
, sal_Int32 nMaxXlsCol
, sal_Int32 nMaxXlsRow
)
465 maMaxXlsPos
.Set( nMaxXlsCol
, nMaxXlsRow
, nMaxXlsTab
);
467 // maximum cell position in Calc
470 Reference
< XIndexAccess
> xSheetsIA( getDocument()->getSheets(), UNO_QUERY_THROW
);
471 Reference
< XCellRangeAddressable
> xAddressable( xSheetsIA
->getByIndex( 0 ), UNO_QUERY_THROW
);
472 CellRangeAddress aRange
= xAddressable
->getRangeAddress();
473 maMaxApiPos
= ScAddress( aRange
.EndColumn
, aRange
.EndRow
, API_MAXTAB
);
474 maMaxPos
= getBaseFilter().isImportFilter() ? maMaxApiPos
: maMaxXlsPos
;
478 OSL_FAIL( "AddressConverter::AddressConverter - cannot get sheet limits" );
485 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */