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>
32 using namespace ::com::sun::star::container
;
33 using namespace ::com::sun::star::sheet
;
34 using namespace ::com::sun::star::table
;
35 using namespace ::com::sun::star::uno
;
39 //! TODO: this limit may change, is there a way to obtain it via API?
40 const sal_Int16 API_MAXTAB
= MAXTAB
;
42 const sal_Int32 OOX_MAXCOL
= static_cast< sal_Int32
>( (1 << 14) - 1 );
43 const sal_Int32 OOX_MAXROW
= static_cast< sal_Int32
>( (1 << 20) - 1 );
44 const sal_Int16 OOX_MAXTAB
= static_cast< sal_Int16
>( (1 << 15) - 1 );
49 void BinAddress::read( SequenceInputStream
& rStrm
)
51 mnRow
= rStrm
.readInt32();
52 mnCol
= rStrm
.readInt32();
55 void BinRange::read( SequenceInputStream
& rStrm
)
57 maFirst
.mnRow
= rStrm
.readInt32();
58 maLast
.mnRow
= rStrm
.readInt32();
59 maFirst
.mnCol
= rStrm
.readInt32();
60 maLast
.mnCol
= rStrm
.readInt32();
63 void BinRangeList::read( SequenceInputStream
& rStrm
)
65 sal_Int32 nCount
= rStrm
.readInt32();
66 mvRanges
.resize( getLimitedValue
< size_t, sal_Int64
>( nCount
, 0, rStrm
.getRemaining() / 16 ) );
67 for( auto& rRange
: mvRanges
)
71 AddressConverter::AddressConverter( const WorkbookHelper
& rHelper
) :
72 WorkbookHelper( rHelper
),
73 mbColOverflow( false ),
74 mbRowOverflow( false ),
75 mbTabOverflow( false )
77 initializeMaxPos( OOX_MAXTAB
, OOX_MAXCOL
, OOX_MAXROW
);
80 bool AddressConverter::parseOoxAddress2d(
81 sal_Int32
& ornColumn
, sal_Int32
& ornRow
,
82 const OUString
& rString
, sal_Int32 nStart
, sal_Int32 nLength
)
84 ornColumn
= ornRow
= 0;
85 if( (nStart
< 0) || (nStart
>= rString
.getLength()) || (nLength
< 2) )
88 const sal_Unicode
* pcChar
= rString
.getStr() + nStart
;
89 const sal_Unicode
* pcEndChar
= pcChar
+ ::std::min( nLength
, rString
.getLength() - nStart
);
91 enum { STATE_COL
, STATE_ROW
} eState
= STATE_COL
;
92 while( pcChar
< pcEndChar
)
94 sal_Unicode cChar
= *pcChar
;
99 if( ('a' <= cChar
) && (cChar
<= 'z') )
100 cChar
= (cChar
- 'a') + 'A';
101 if( ('A' <= cChar
) && (cChar
<= 'Z') )
103 /* Return, if 1-based column index is already 6 characters
104 long (12356631 is column index for column AAAAAA). */
105 if( ornColumn
>= 12356631 )
107 ornColumn
= (ornColumn
* 26) + (cChar
- 'A' + 1);
109 else if( ornColumn
> 0 )
121 if( ('0' <= cChar
) && (cChar
<= '9') )
123 // return, if 1-based row is already 9 digits long
124 if( ornRow
>= 100000000 )
126 ornRow
= (ornRow
* 10) + (cChar
- '0');
138 return (ornColumn
>= 0) && (ornRow
>= 0);
141 bool AddressConverter::parseOoxAddress2d( sal_Int32
& ornColumn
, sal_Int32
& ornRow
, const char* pStr
)
143 ornColumn
= ornRow
= 0;
145 enum { STATE_COL
, STATE_ROW
} eState
= STATE_COL
;
154 if( ('a' <= cChar
) && (cChar
<= 'z') )
155 cChar
= (cChar
- 'a') + 'A';
156 if( ('A' <= cChar
) && (cChar
<= 'Z') )
158 /* Return, if 1-based column index is already 6 characters
159 long (12356631 is column index for column AAAAAA). */
160 if( ornColumn
>= 12356631 )
162 ornColumn
= (ornColumn
* 26) + (cChar
- 'A' + 1);
164 else if( ornColumn
> 0 )
176 if( ('0' <= cChar
) && (cChar
<= '9') )
178 // return, if 1-based row is already 9 digits long
179 if( ornRow
>= 100000000 )
181 ornRow
= (ornRow
* 10) + (cChar
- '0');
193 return (ornColumn
>= 0) && (ornRow
>= 0);
196 bool AddressConverter::parseOoxRange2d(
197 sal_Int32
& ornStartColumn
, sal_Int32
& ornStartRow
,
198 sal_Int32
& ornEndColumn
, sal_Int32
& ornEndRow
,
199 const OUString
& rString
, sal_Int32 nStart
)
201 ornStartColumn
= ornStartRow
= ornEndColumn
= ornEndRow
= 0;
202 if( (nStart
< 0) || (nStart
>= rString
.getLength()) )
205 sal_Int32 nEnd
= nStart
+ ( rString
.getLength() - nStart
);
206 sal_Int32 nColonPos
= rString
.indexOf( ':', nStart
);
207 if( (nStart
< nColonPos
) && (nColonPos
+ 1 < nEnd
) )
210 parseOoxAddress2d( ornStartColumn
, ornStartRow
, rString
, nStart
, nColonPos
- nStart
) &&
211 parseOoxAddress2d( ornEndColumn
, ornEndRow
, rString
, nColonPos
+ 1, SAL_MAX_INT32
- nColonPos
- 1 );
214 if( parseOoxAddress2d( ornStartColumn
, ornStartRow
, rString
, nStart
) )
216 ornEndColumn
= ornStartColumn
;
217 ornEndRow
= ornStartRow
;
224 bool AddressConverter::checkCol( sal_Int32 nCol
, bool bTrackOverflow
)
226 bool bValid
= (0 <= nCol
) && ( nCol
<= maMaxPos
.Col() );
227 if( !bValid
&& bTrackOverflow
)
228 mbColOverflow
= true;
232 bool AddressConverter::checkRow( sal_Int32 nRow
, bool bTrackOverflow
)
234 bool bValid
= (0 <= nRow
) && ( nRow
<= maMaxPos
.Row() );
235 if( !bValid
&& bTrackOverflow
)
236 mbRowOverflow
= true;
240 bool AddressConverter::checkTab( sal_Int16 nSheet
, bool bTrackOverflow
)
242 bool bValid
= (0 <= nSheet
) && ( nSheet
<= maMaxPos
.Tab() );
243 if( !bValid
&& bTrackOverflow
)
244 mbTabOverflow
|= ( nSheet
> maMaxPos
.Tab() ); // do not warn for deleted refs (-1)
248 bool AddressConverter::checkCellAddress( const ScAddress
& rAddress
, bool bTrackOverflow
)
251 checkTab( rAddress
.Tab(), bTrackOverflow
) &&
252 checkCol( rAddress
.Col(), bTrackOverflow
) &&
253 checkRow( rAddress
.Row(), bTrackOverflow
);
256 bool AddressConverter::convertToCellAddressUnchecked( ScAddress
& orAddress
,
257 const OUString
& rString
, sal_Int16 nSheet
)
259 orAddress
.SetTab(nSheet
);
262 bool bRes
= parseOoxAddress2d( nCol
, nRow
, rString
);
263 orAddress
.SetRow(nRow
);
264 orAddress
.SetCol(nCol
);
269 bool AddressConverter::convertToCellAddressUnchecked(
270 ScAddress
& orAddress
, const char* pStr
, sal_Int16 nSheet
)
272 orAddress
.SetTab(nSheet
);
275 bool bRes
= parseOoxAddress2d(nCol
, nRow
, pStr
);
276 orAddress
.SetRow(nRow
);
277 orAddress
.SetCol(nCol
);
282 bool AddressConverter::convertToCellAddress( ScAddress
& orAddress
,
283 const OUString
& rString
, sal_Int16 nSheet
, bool bTrackOverflow
)
286 convertToCellAddressUnchecked( orAddress
, rString
, nSheet
) &&
287 checkCellAddress( orAddress
, bTrackOverflow
);
290 bool AddressConverter::convertToCellAddress(
292 const char* pStr
, sal_Int16 nSheet
, bool bTrackOverflow
)
294 if (!convertToCellAddressUnchecked(rAddress
, pStr
, nSheet
))
297 return checkCellAddress(rAddress
, bTrackOverflow
);
300 ScAddress
AddressConverter::createValidCellAddress(
301 const OUString
& rString
, sal_Int16 nSheet
, bool bTrackOverflow
)
303 ScAddress
aAddress( 0, 0, 0 );
304 if( !convertToCellAddress( aAddress
, rString
, nSheet
, bTrackOverflow
) )
306 aAddress
.SetTab( getLimitedValue
< sal_Int16
, sal_Int16
>( nSheet
, 0, maMaxPos
.Tab() ) );
307 aAddress
.SetCol( ::std::min( aAddress
.Col(), maMaxPos
.Col() ) );
308 aAddress
.SetRow( ::std::min( aAddress
.Row(), maMaxPos
.Row() ) );
313 void AddressConverter::convertToCellAddressUnchecked( ScAddress
& orAddress
,
314 const BinAddress
& rBinAddress
, sal_Int16 nSheet
)
316 orAddress
.SetTab(nSheet
);
317 orAddress
.SetCol(rBinAddress
.mnCol
);
318 orAddress
.SetRow(rBinAddress
.mnRow
);
321 bool AddressConverter::convertToCellAddress( ScAddress
& orAddress
,
322 const BinAddress
& rBinAddress
, sal_Int16 nSheet
, bool bTrackOverflow
)
324 convertToCellAddressUnchecked( orAddress
, rBinAddress
, nSheet
);
325 return checkCellAddress( orAddress
, bTrackOverflow
);
328 ScAddress
AddressConverter::createValidCellAddress(
329 const BinAddress
& rBinAddress
, sal_Int16 nSheet
, bool bTrackOverflow
)
331 ScAddress
aAddress ( 0, 0, 0 );
332 if( !convertToCellAddress( aAddress
, rBinAddress
, nSheet
, bTrackOverflow
) )
334 aAddress
.SetTab( getLimitedValue
< sal_Int16
, sal_Int16
>( nSheet
, 0, maMaxPos
.Tab() ) );
335 aAddress
.SetCol( getLimitedValue
< sal_Int32
, sal_Int32
>( rBinAddress
.mnCol
, 0, sal_Int32( maMaxPos
.Col() ) ) );
336 aAddress
.SetRow( getLimitedValue
< sal_Int32
, sal_Int32
>( rBinAddress
.mnRow
, 0, sal_Int32( maMaxPos
.Row() ) ) );
341 bool AddressConverter::checkCellRange( const ScRange
& rRange
, bool bAllowOverflow
, bool bTrackOverflow
)
344 (checkCol( rRange
.aEnd
.Col(), bTrackOverflow
) || bAllowOverflow
) && // bAllowOverflow after checkCol to track overflow!
345 (checkRow( rRange
.aEnd
.Row(), bTrackOverflow
) || bAllowOverflow
) && // bAllowOverflow after checkRow to track overflow!
346 checkTab( rRange
.aStart
.Tab(), bTrackOverflow
) &&
347 checkCol( rRange
.aStart
.Col(), bTrackOverflow
) &&
348 checkRow( rRange
.aStart
.Row(), bTrackOverflow
);
351 bool AddressConverter::validateCellRange( ScRange
& orRange
, bool bAllowOverflow
, bool bTrackOverflow
)
353 if( orRange
.aStart
.Col() > orRange
.aEnd
.Col() )
355 SCCOL nCol
= orRange
.aStart
.Col();
356 orRange
.aStart
.SetCol( orRange
.aEnd
.Col() );
357 orRange
.aEnd
.SetCol( nCol
);
359 if( orRange
.aStart
.Row() > orRange
.aEnd
.Row() )
361 SCROW nRow
= orRange
.aStart
.Row();
362 orRange
.aStart
.SetRow( orRange
.aEnd
.Row() );
363 orRange
.aEnd
.SetRow( nRow
);
365 if( !checkCellRange( orRange
, bAllowOverflow
, bTrackOverflow
) )
367 if( orRange
.aEnd
.Col() > maMaxPos
.Col() )
368 orRange
.aEnd
.SetCol( maMaxPos
.Col() );
369 if( orRange
.aEnd
.Row() > maMaxPos
.Row() )
370 orRange
.aEnd
.SetRow( maMaxPos
.Row() );
374 bool AddressConverter::convertToCellRangeUnchecked( ScRange
& orRange
,
375 const OUString
& rString
, sal_Int16 nSheet
)
377 orRange
.aStart
.SetTab( nSheet
);
378 orRange
.aEnd
.SetTab( nSheet
);
379 sal_Int32 aStartCol
= orRange
.aStart
.Col();
380 sal_Int32 aStartRow
= orRange
.aStart
.Row();
381 sal_Int32 aEndCol
= orRange
.aEnd
.Col();
382 sal_Int32 aEndRow
= orRange
.aEnd
.Row();
383 bool bReturnValue
= parseOoxRange2d( aStartCol
, aStartRow
, aEndCol
, aEndRow
, rString
);
384 orRange
.aStart
.SetCol( aStartCol
);
385 orRange
.aStart
.SetRow( aStartRow
);
386 orRange
.aEnd
.SetCol( aEndCol
);
387 orRange
.aEnd
.SetRow( aEndRow
);
391 bool AddressConverter::convertToCellRange( ScRange
& orRange
,
392 const OUString
& rString
, sal_Int16 nSheet
, bool bAllowOverflow
, bool bTrackOverflow
)
395 convertToCellRangeUnchecked( orRange
, rString
, nSheet
) &&
396 validateCellRange( orRange
, bAllowOverflow
, bTrackOverflow
);
399 void AddressConverter::convertToCellRangeUnchecked( ScRange
& orRange
,
400 const BinRange
& rBinRange
, sal_Int16 nSheet
)
402 orRange
.aStart
.SetTab( nSheet
);
403 orRange
.aStart
.SetCol( rBinRange
.maFirst
.mnCol
);
404 orRange
.aStart
.SetRow( rBinRange
.maFirst
.mnRow
);
405 orRange
.aEnd
.SetTab( nSheet
);
406 orRange
.aEnd
.SetCol( rBinRange
.maLast
.mnCol
);
407 orRange
.aEnd
.SetRow( rBinRange
.maLast
.mnRow
);
410 bool AddressConverter::convertToCellRange( ScRange
& orRange
,
411 const BinRange
& rBinRange
, sal_Int16 nSheet
, bool bAllowOverflow
, bool bTrackOverflow
)
413 convertToCellRangeUnchecked( orRange
, rBinRange
, nSheet
);
414 return validateCellRange( orRange
, bAllowOverflow
, bTrackOverflow
);
417 void AddressConverter::validateCellRangeList( ScRangeList
& orRanges
, bool bTrackOverflow
)
419 for( size_t nIndex
= orRanges
.size(); nIndex
> 0; --nIndex
)
420 if( !validateCellRange( orRanges
[ nIndex
- 1 ], true, bTrackOverflow
) )
421 orRanges
.Remove( nIndex
- 1 );
424 void AddressConverter::convertToCellRangeList( ScRangeList
& orRanges
,
425 const OUString
& rString
, sal_Int16 nSheet
, bool bTrackOverflow
)
428 sal_Int32 nLen
= rString
.getLength();
430 while( (0 <= nPos
) && (nPos
< nLen
) )
432 OUString aToken
= rString
.getToken( 0, ' ', nPos
);
433 if( !aToken
.isEmpty() && convertToCellRange( aRange
, aToken
, nSheet
, true, bTrackOverflow
) )
434 orRanges
.push_back(aRange
);
438 void AddressConverter::convertToCellRangeList( ScRangeList
& orRanges
,
439 const BinRangeList
& rBinRanges
, sal_Int16 nSheet
, bool bTrackOverflow
)
442 for( const auto& rBinRange
: rBinRanges
)
443 if( convertToCellRange( aRange
, rBinRange
, nSheet
, true, bTrackOverflow
) )
444 orRanges
.push_back( aRange
);
447 Sequence
<CellRangeAddress
> AddressConverter::toApiSequence(const ScRangeList
& orRanges
)
449 const size_t nSize
= orRanges
.size();
450 Sequence
<CellRangeAddress
> aRangeSequence(nSize
);
451 CellRangeAddress
* pApiRanges
= aRangeSequence
.getArray();
452 for (size_t i
= 0; i
< nSize
; ++i
)
454 ScUnoConversion::FillApiRange(pApiRanges
[i
], orRanges
[i
]);
456 return aRangeSequence
;
459 // private --------------------------------------------------------------------
461 void AddressConverter::initializeMaxPos(
462 sal_Int16 nMaxXlsTab
, sal_Int32 nMaxXlsCol
, sal_Int32 nMaxXlsRow
)
464 maMaxXlsPos
.Set( nMaxXlsCol
, nMaxXlsRow
, nMaxXlsTab
);
466 // maximum cell position in Calc
469 Reference
< XIndexAccess
> xSheetsIA( getDocument()->getSheets(), UNO_QUERY_THROW
);
470 Reference
< XCellRangeAddressable
> xAddressable( xSheetsIA
->getByIndex( 0 ), UNO_QUERY_THROW
);
471 CellRangeAddress aRange
= xAddressable
->getRangeAddress();
472 maMaxApiPos
= ScAddress( aRange
.EndColumn
, aRange
.EndRow
, API_MAXTAB
);
473 maMaxPos
= getBaseFilter().isImportFilter() ? maMaxApiPos
: maMaxXlsPos
;
477 OSL_FAIL( "AddressConverter::AddressConverter - cannot get sheet limits" );
483 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */