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 .
21 #include "XMLRangeHelper.hxx"
22 #include <unotools/charclass.hxx>
23 #include <rtl/ustrbuf.hxx>
29 // ================================================================================
33 /** unary function that escapes backslashes and single quotes in a sal_Unicode
34 array (which you can get from an OUString with getStr()) and puts the result
35 into the OUStringBuffer given in the CTOR
37 class lcl_Escape
: public ::std::unary_function
< sal_Unicode
, void >
40 lcl_Escape( OUStringBuffer
& aResultBuffer
) : m_aResultBuffer( aResultBuffer
) {}
41 void operator() ( sal_Unicode aChar
)
43 static const sal_Unicode
m_aQuote( '\'' );
44 static const sal_Unicode
m_aBackslash( '\\' );
46 if( aChar
== m_aQuote
||
47 aChar
== m_aBackslash
)
48 m_aResultBuffer
.append( m_aBackslash
);
49 m_aResultBuffer
.append( aChar
);
53 OUStringBuffer
& m_aResultBuffer
;
56 // ----------------------------------------
58 /** unary function that removes backslash escapes in a sal_Unicode array (which
59 you can get from an OUString with getStr()) and puts the result into the
60 OUStringBuffer given in the CTOR
62 class lcl_UnEscape
: public ::std::unary_function
< sal_Unicode
, void >
65 lcl_UnEscape( OUStringBuffer
& aResultBuffer
) : m_aResultBuffer( aResultBuffer
) {}
66 void operator() ( sal_Unicode aChar
)
68 static const sal_Unicode
m_aBackslash( '\\' );
70 if( aChar
!= m_aBackslash
)
71 m_aResultBuffer
.append( aChar
);
75 OUStringBuffer
& m_aResultBuffer
;
78 // ----------------------------------------
80 void lcl_getXMLStringForCell( const ::chart::XMLRangeHelper::Cell
& rCell
, OUStringBuffer
* output
)
82 OSL_ASSERT(output
!= 0);
87 sal_Int32 nCol
= rCell
.nColumn
;
88 output
->append( (sal_Unicode
)'.' );
89 if( ! rCell
.bRelativeColumn
)
90 output
->append( (sal_Unicode
)'$' );
92 // get A, B, C, ..., AA, AB, ... representation of column number
94 output
->append( (sal_Unicode
)('A' + nCol
) );
97 output
->append( (sal_Unicode
)('A' + nCol
/ 26 - 1 ));
98 output
->append( (sal_Unicode
)('A' + nCol
% 26) );
100 else // works for nCol <= 18,278
102 output
->append( (sal_Unicode
)('A' + nCol
/ 702 - 1 ));
103 output
->append( (sal_Unicode
)('A' + (nCol
% 702) / 26 ));
104 output
->append( (sal_Unicode
)('A' + nCol
% 26) );
107 // write row number as number
108 if( ! rCell
.bRelativeRow
)
109 output
->append( (sal_Unicode
)'$' );
110 output
->append( rCell
.nRow
+ (sal_Int32
)1 );
113 void lcl_getSingleCellAddressFromXMLString(
114 const OUString
& rXMLString
,
115 sal_Int32 nStartPos
, sal_Int32 nEndPos
,
116 ::chart::XMLRangeHelper::Cell
& rOutCell
)
118 // expect "\$?[a-zA-Z]+\$?[1-9][0-9]*"
119 static const sal_Unicode
aDollar( '$' );
120 static const sal_Unicode
aLetterA( 'A' );
122 OUString aCellStr
= rXMLString
.copy( nStartPos
, nEndPos
- nStartPos
+ 1 ).toAsciiUpperCase();
123 const sal_Unicode
* pStrArray
= aCellStr
.getStr();
124 sal_Int32 nLength
= aCellStr
.getLength();
125 sal_Int32 i
= nLength
- 1, nColumn
= 0;
127 // parse number for row
128 while( CharClass::isAsciiDigit( pStrArray
[ i
] ) && i
>= 0 )
130 rOutCell
.nRow
= (aCellStr
.copy( i
+ 1 )).toInt32() - 1;
131 // a dollar in XML means absolute (whereas in UI it means relative)
132 if( pStrArray
[ i
] == aDollar
)
135 rOutCell
.bRelativeRow
= false;
138 rOutCell
.bRelativeRow
= true;
140 // parse rest for column
141 sal_Int32 nPower
= 1;
142 while( CharClass::isAsciiAlpha( pStrArray
[ i
] ))
144 nColumn
+= (pStrArray
[ i
] - aLetterA
+ 1) * nPower
;
148 rOutCell
.nColumn
= nColumn
- 1;
150 rOutCell
.bRelativeColumn
= true;
152 pStrArray
[ i
] == aDollar
)
153 rOutCell
.bRelativeColumn
= false;
154 rOutCell
.bIsEmpty
= false;
157 bool lcl_getCellAddressFromXMLString(
158 const OUString
& rXMLString
,
159 sal_Int32 nStartPos
, sal_Int32 nEndPos
,
160 ::chart::XMLRangeHelper::Cell
& rOutCell
,
161 OUString
& rOutTableName
)
163 static const sal_Unicode
aDot( '.' );
164 static const sal_Unicode
aQuote( '\'' );
165 static const sal_Unicode
aBackslash( '\\' );
167 sal_Int32 nNextDelimiterPos
= nStartPos
;
169 sal_Int32 nDelimiterPos
= nStartPos
;
170 bool bInQuotation
= false;
172 while( nDelimiterPos
< nEndPos
&&
173 ( bInQuotation
|| rXMLString
[ nDelimiterPos
] != aDot
))
175 // skip escaped characters (with backslash)
176 if( rXMLString
[ nDelimiterPos
] == aBackslash
)
178 // toggle quotation mode when finding single quotes
179 else if( rXMLString
[ nDelimiterPos
] == aQuote
)
180 bInQuotation
= ! bInQuotation
;
185 if( nDelimiterPos
== -1 )
188 if( nDelimiterPos
> nStartPos
&& nDelimiterPos
< nEndPos
)
190 // there is a table name before the address
192 OUStringBuffer aTableNameBuffer
;
193 const sal_Unicode
* pTableName
= rXMLString
.getStr();
195 // remove escapes from table name
196 ::std::for_each( pTableName
+ nStartPos
,
197 pTableName
+ nDelimiterPos
,
198 lcl_UnEscape( aTableNameBuffer
));
200 // unquote quoted table name
201 const sal_Unicode
* pBuf
= aTableNameBuffer
.getStr();
202 if( pBuf
[ 0 ] == aQuote
&&
203 pBuf
[ aTableNameBuffer
.getLength() - 1 ] == aQuote
)
205 OUString aName
= aTableNameBuffer
.makeStringAndClear();
206 rOutTableName
= aName
.copy( 1, aName
.getLength() - 2 );
209 rOutTableName
= aTableNameBuffer
.makeStringAndClear();
212 nDelimiterPos
= nStartPos
;
214 for( sal_Int32 i
= 0;
215 nNextDelimiterPos
< nEndPos
;
216 nDelimiterPos
= nNextDelimiterPos
, i
++ )
218 nNextDelimiterPos
= rXMLString
.indexOf( aDot
, nDelimiterPos
+ 1 );
219 if( nNextDelimiterPos
== -1 ||
220 nNextDelimiterPos
> nEndPos
)
221 nNextDelimiterPos
= nEndPos
+ 1;
224 // only take first cell
225 lcl_getSingleCellAddressFromXMLString(
226 rXMLString
, nDelimiterPos
+ 1, nNextDelimiterPos
- 1, rOutCell
);
232 bool lcl_getCellRangeAddressFromXMLString(
233 const OUString
& rXMLString
,
234 sal_Int32 nStartPos
, sal_Int32 nEndPos
,
235 ::chart::XMLRangeHelper::CellRange
& rOutRange
)
238 static const sal_Unicode
aColon( ':' );
239 static const sal_Unicode
aQuote( '\'' );
240 static const sal_Unicode
aBackslash( '\\' );
242 sal_Int32 nDelimiterPos
= nStartPos
;
243 bool bInQuotation
= false;
245 while( nDelimiterPos
< nEndPos
&&
246 ( bInQuotation
|| rXMLString
[ nDelimiterPos
] != aColon
))
248 // skip escaped characters (with backslash)
249 if( rXMLString
[ nDelimiterPos
] == aBackslash
)
251 // toggle quotation mode when finding single quotes
252 else if( rXMLString
[ nDelimiterPos
] == aQuote
)
253 bInQuotation
= ! bInQuotation
;
258 if( nDelimiterPos
== nEndPos
)
261 bResult
= lcl_getCellAddressFromXMLString( rXMLString
, nStartPos
, nEndPos
,
262 rOutRange
.aUpperLeft
,
263 rOutRange
.aTableName
);
264 if( rOutRange
.aTableName
.isEmpty() )
269 // range (separated by a colon)
270 bResult
= lcl_getCellAddressFromXMLString( rXMLString
, nStartPos
, nDelimiterPos
- 1,
271 rOutRange
.aUpperLeft
,
272 rOutRange
.aTableName
);
273 if( rOutRange
.aTableName
.isEmpty() )
276 OUString sTableSecondName
;
279 bResult
= lcl_getCellAddressFromXMLString( rXMLString
, nDelimiterPos
+ 1, nEndPos
,
280 rOutRange
.aLowerRight
,
284 !sTableSecondName
.isEmpty() &&
285 ! sTableSecondName
.equals( rOutRange
.aTableName
))
292 } // anonymous namespace
294 // ================================================================================
298 namespace XMLRangeHelper
301 CellRange
getCellRangeFromXMLString( const OUString
& rXMLString
)
303 static const sal_Unicode
aSpace( ' ' );
304 static const sal_Unicode
aQuote( '\'' );
305 // static const sal_Unicode aDoubleQuote( '\"' );
306 static const sal_Unicode
aDollar( '$' );
307 static const sal_Unicode
aBackslash( '\\' );
309 sal_Int32 nStartPos
= 0;
310 sal_Int32 nEndPos
= nStartPos
;
311 const sal_Int32 nLength
= rXMLString
.getLength();
316 // iterate over different ranges
317 for( sal_Int32 i
= 0;
319 nStartPos
= ++nEndPos
, i
++ )
321 // find start point of next range
323 // ignore leading '$'
324 if( rXMLString
[ nEndPos
] == aDollar
)
327 bool bInQuotation
= false;
329 while( nEndPos
< nLength
&&
330 ( bInQuotation
|| rXMLString
[ nEndPos
] != aSpace
))
332 // skip escaped characters (with backslash)
333 if( rXMLString
[ nEndPos
] == aBackslash
)
335 // toggle quotation mode when finding single quotes
336 else if( rXMLString
[ nEndPos
] == aQuote
)
337 bInQuotation
= ! bInQuotation
;
342 if( ! lcl_getCellRangeAddressFromXMLString(
344 nStartPos
, nEndPos
- 1,
347 // if an error occurred, bail out
355 OUString
getXMLStringFromCellRange( const CellRange
& rRange
)
357 static const sal_Unicode
aSpace( ' ' );
358 static const sal_Unicode
aQuote( '\'' );
360 OUStringBuffer aBuffer
;
362 if( !(rRange
.aTableName
).isEmpty())
364 bool bNeedsEscaping
= ( rRange
.aTableName
.indexOf( aQuote
) > -1 );
365 bool bNeedsQuoting
= bNeedsEscaping
|| ( rRange
.aTableName
.indexOf( aSpace
) > -1 );
367 // quote table name if it contains spaces or quotes
371 aBuffer
.append( aQuote
);
373 // escape existing quotes
376 const sal_Unicode
* pTableNameBeg
= rRange
.aTableName
.getStr();
378 // append the quoted string at the buffer
379 ::std::for_each( pTableNameBeg
,
380 pTableNameBeg
+ rRange
.aTableName
.getLength(),
381 lcl_Escape( aBuffer
) );
384 aBuffer
.append( rRange
.aTableName
);
387 aBuffer
.append( aQuote
);
390 aBuffer
.append( rRange
.aTableName
);
392 lcl_getXMLStringForCell( rRange
.aUpperLeft
, &aBuffer
);
394 if( ! rRange
.aLowerRight
.empty())
396 // we have a range (not a single cell)
397 aBuffer
.append( sal_Unicode( ':' ));
398 lcl_getXMLStringForCell( rRange
.aLowerRight
, &aBuffer
);
401 return aBuffer
.makeStringAndClear();
404 } // namespace XMLRangeHelper
407 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */