1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2000, 2010 Oracle and/or its affiliates.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * This file is part of OpenOffice.org.
11 * OpenOffice.org is free software: you can redistribute it and/or modify
12 * it under the terms of the GNU Lesser General Public License version 3
13 * only, as published by the Free Software Foundation.
15 * OpenOffice.org is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Lesser General Public License version 3 for more details
19 * (a copy is included in the LICENSE file that accompanied this code).
21 * You should have received a copy of the GNU Lesser General Public License
22 * version 3 along with OpenOffice.org. If not, see
23 * <http://www.openoffice.org/license.html>
24 * for a copy of the LGPLv3 License.
26 ************************************************************************/
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_chart2.hxx"
31 #include "XMLRangeHelper.hxx"
32 #include <unotools/charclass.hxx>
33 #include <rtl/ustrbuf.hxx>
38 using ::rtl::OUString
;
39 using ::rtl::OUStringBuffer
;
41 // ================================================================================
45 /** unary function that escapes backslashes and single quotes in a sal_Unicode
46 array (which you can get from an OUString with getStr()) and puts the result
47 into the OUStringBuffer given in the CTOR
49 class lcl_Escape
: public ::std::unary_function
< sal_Unicode
, void >
52 lcl_Escape( ::rtl::OUStringBuffer
& aResultBuffer
) : m_aResultBuffer( aResultBuffer
) {}
53 void operator() ( sal_Unicode aChar
)
55 static const sal_Unicode
m_aQuote( '\'' );
56 static const sal_Unicode
m_aBackslash( '\\' );
58 if( aChar
== m_aQuote
||
59 aChar
== m_aBackslash
)
60 m_aResultBuffer
.append( m_aBackslash
);
61 m_aResultBuffer
.append( aChar
);
65 ::rtl::OUStringBuffer
& m_aResultBuffer
;
68 // ----------------------------------------
70 /** unary function that removes backslash escapes in a sal_Unicode array (which
71 you can get from an OUString with getStr()) and puts the result into the
72 OUStringBuffer given in the CTOR
74 class lcl_UnEscape
: public ::std::unary_function
< sal_Unicode
, void >
77 lcl_UnEscape( ::rtl::OUStringBuffer
& aResultBuffer
) : m_aResultBuffer( aResultBuffer
) {}
78 void operator() ( sal_Unicode aChar
)
80 static const sal_Unicode
m_aBackslash( '\\' );
82 if( aChar
!= m_aBackslash
)
83 m_aResultBuffer
.append( aChar
);
87 ::rtl::OUStringBuffer
& m_aResultBuffer
;
90 // ----------------------------------------
92 OUStringBuffer
lcl_getXMLStringForCell( const ::chart::XMLRangeHelper::Cell
& rCell
)
94 ::rtl::OUStringBuffer aBuffer
;
98 sal_Int32 nCol
= rCell
.nColumn
;
99 aBuffer
.append( (sal_Unicode
)'.' );
100 if( ! rCell
.bRelativeColumn
)
101 aBuffer
.append( (sal_Unicode
)'$' );
103 // get A, B, C, ..., AA, AB, ... representation of column number
105 aBuffer
.append( (sal_Unicode
)('A' + nCol
) );
106 else if( nCol
< 702 )
108 aBuffer
.append( (sal_Unicode
)('A' + nCol
/ 26 - 1 ));
109 aBuffer
.append( (sal_Unicode
)('A' + nCol
% 26) );
111 else // works for nCol <= 18,278
113 aBuffer
.append( (sal_Unicode
)('A' + nCol
/ 702 - 1 ));
114 aBuffer
.append( (sal_Unicode
)('A' + (nCol
% 702) / 26 ));
115 aBuffer
.append( (sal_Unicode
)('A' + nCol
% 26) );
118 // write row number as number
119 if( ! rCell
.bRelativeRow
)
120 aBuffer
.append( (sal_Unicode
)'$' );
121 aBuffer
.append( rCell
.nRow
+ (sal_Int32
)1 );
126 void lcl_getSingleCellAddressFromXMLString(
127 const ::rtl::OUString
& rXMLString
,
128 sal_Int32 nStartPos
, sal_Int32 nEndPos
,
129 ::chart::XMLRangeHelper::Cell
& rOutCell
)
131 // expect "\$?[a-zA-Z]+\$?[1-9][0-9]*"
132 static const sal_Unicode
aDollar( '$' );
133 static const sal_Unicode
aLetterA( 'A' );
135 ::rtl::OUString aCellStr
= rXMLString
.copy( nStartPos
, nEndPos
- nStartPos
+ 1 ).toAsciiUpperCase();
136 const sal_Unicode
* pStrArray
= aCellStr
.getStr();
137 sal_Int32 nLength
= aCellStr
.getLength();
138 sal_Int32 i
= nLength
- 1, nColumn
= 0;
140 // parse number for row
141 while( CharClass::isAsciiDigit( pStrArray
[ i
] ) && i
>= 0 )
143 rOutCell
.nRow
= (aCellStr
.copy( i
+ 1 )).toInt32() - 1;
144 // a dollar in XML means absolute (whereas in UI it means relative)
145 if( pStrArray
[ i
] == aDollar
)
148 rOutCell
.bRelativeRow
= false;
151 rOutCell
.bRelativeRow
= true;
153 // parse rest for column
154 sal_Int32 nPower
= 1;
155 while( CharClass::isAsciiAlpha( pStrArray
[ i
] ))
157 nColumn
+= (pStrArray
[ i
] - aLetterA
+ 1) * nPower
;
161 rOutCell
.nColumn
= nColumn
- 1;
163 rOutCell
.bRelativeColumn
= true;
165 pStrArray
[ i
] == aDollar
)
166 rOutCell
.bRelativeColumn
= false;
167 rOutCell
.bIsEmpty
= false;
170 bool lcl_getCellAddressFromXMLString(
171 const ::rtl::OUString
& rXMLString
,
172 sal_Int32 nStartPos
, sal_Int32 nEndPos
,
173 ::chart::XMLRangeHelper::Cell
& rOutCell
,
174 ::rtl::OUString
& rOutTableName
)
176 static const sal_Unicode
aDot( '.' );
177 static const sal_Unicode
aQuote( '\'' );
178 static const sal_Unicode
aBackslash( '\\' );
180 sal_Int32 nNextDelimiterPos
= nStartPos
;
182 sal_Int32 nDelimiterPos
= nStartPos
;
183 bool bInQuotation
= false;
185 while( nDelimiterPos
< nEndPos
&&
186 ( bInQuotation
|| rXMLString
[ nDelimiterPos
] != aDot
))
188 // skip escaped characters (with backslash)
189 if( rXMLString
[ nDelimiterPos
] == aBackslash
)
191 // toggle quotation mode when finding single quotes
192 else if( rXMLString
[ nDelimiterPos
] == aQuote
)
193 bInQuotation
= ! bInQuotation
;
198 if( nDelimiterPos
== -1 )
201 if( nDelimiterPos
> nStartPos
&& nDelimiterPos
< nEndPos
)
203 // there is a table name before the address
205 ::rtl::OUStringBuffer aTableNameBuffer
;
206 const sal_Unicode
* pTableName
= rXMLString
.getStr();
208 // remove escapes from table name
209 ::std::for_each( pTableName
+ nStartPos
,
210 pTableName
+ nDelimiterPos
,
211 lcl_UnEscape( aTableNameBuffer
));
213 // unquote quoted table name
214 const sal_Unicode
* pBuf
= aTableNameBuffer
.getStr();
215 if( pBuf
[ 0 ] == aQuote
&&
216 pBuf
[ aTableNameBuffer
.getLength() - 1 ] == aQuote
)
218 ::rtl::OUString aName
= aTableNameBuffer
.makeStringAndClear();
219 rOutTableName
= aName
.copy( 1, aName
.getLength() - 2 );
222 rOutTableName
= aTableNameBuffer
.makeStringAndClear();
225 nDelimiterPos
= nStartPos
;
227 for( sal_Int32 i
= 0;
228 nNextDelimiterPos
< nEndPos
;
229 nDelimiterPos
= nNextDelimiterPos
, i
++ )
231 nNextDelimiterPos
= rXMLString
.indexOf( aDot
, nDelimiterPos
+ 1 );
232 if( nNextDelimiterPos
== -1 ||
233 nNextDelimiterPos
> nEndPos
)
234 nNextDelimiterPos
= nEndPos
+ 1;
237 // only take first cell
238 lcl_getSingleCellAddressFromXMLString(
239 rXMLString
, nDelimiterPos
+ 1, nNextDelimiterPos
- 1, rOutCell
);
245 bool lcl_getCellRangeAddressFromXMLString(
246 const ::rtl::OUString
& rXMLString
,
247 sal_Int32 nStartPos
, sal_Int32 nEndPos
,
248 ::chart::XMLRangeHelper::CellRange
& rOutRange
)
251 static const sal_Unicode
aColon( ':' );
252 static const sal_Unicode
aQuote( '\'' );
253 static const sal_Unicode
aBackslash( '\\' );
255 sal_Int32 nDelimiterPos
= nStartPos
;
256 bool bInQuotation
= false;
258 while( nDelimiterPos
< nEndPos
&&
259 ( bInQuotation
|| rXMLString
[ nDelimiterPos
] != aColon
))
261 // skip escaped characters (with backslash)
262 if( rXMLString
[ nDelimiterPos
] == aBackslash
)
264 // toggle quotation mode when finding single quotes
265 else if( rXMLString
[ nDelimiterPos
] == aQuote
)
266 bInQuotation
= ! bInQuotation
;
271 if( nDelimiterPos
== nEndPos
)
274 bResult
= lcl_getCellAddressFromXMLString( rXMLString
, nStartPos
, nEndPos
,
275 rOutRange
.aUpperLeft
,
276 rOutRange
.aTableName
);
277 if( !rOutRange
.aTableName
.getLength() )
282 // range (separated by a colon)
283 bResult
= lcl_getCellAddressFromXMLString( rXMLString
, nStartPos
, nDelimiterPos
- 1,
284 rOutRange
.aUpperLeft
,
285 rOutRange
.aTableName
);
286 if( !rOutRange
.aTableName
.getLength() )
289 ::rtl::OUString sTableSecondName
;
292 bResult
= lcl_getCellAddressFromXMLString( rXMLString
, nDelimiterPos
+ 1, nEndPos
,
293 rOutRange
.aLowerRight
,
297 sTableSecondName
.getLength() &&
298 ! sTableSecondName
.equals( rOutRange
.aTableName
))
305 } // anonymous namespace
307 // ================================================================================
311 namespace XMLRangeHelper
314 CellRange
getCellRangeFromXMLString( const OUString
& rXMLString
)
316 static const sal_Unicode
aSpace( ' ' );
317 static const sal_Unicode
aQuote( '\'' );
318 // static const sal_Unicode aDoubleQuote( '\"' );
319 static const sal_Unicode
aDollar( '$' );
320 static const sal_Unicode
aBackslash( '\\' );
322 sal_Int32 nStartPos
= 0;
323 sal_Int32 nEndPos
= nStartPos
;
324 const sal_Int32 nLength
= rXMLString
.getLength();
329 // iterate over different ranges
330 for( sal_Int32 i
= 0;
332 nStartPos
= ++nEndPos
, i
++ )
334 // find start point of next range
336 // ignore leading '$'
337 if( rXMLString
[ nEndPos
] == aDollar
)
340 bool bInQuotation
= false;
342 while( nEndPos
< nLength
&&
343 ( bInQuotation
|| rXMLString
[ nEndPos
] != aSpace
))
345 // skip escaped characters (with backslash)
346 if( rXMLString
[ nEndPos
] == aBackslash
)
348 // toggle quotation mode when finding single quotes
349 else if( rXMLString
[ nEndPos
] == aQuote
)
350 bInQuotation
= ! bInQuotation
;
355 if( ! lcl_getCellRangeAddressFromXMLString(
357 nStartPos
, nEndPos
- 1,
360 // if an error occured, bail out
368 OUString
getXMLStringFromCellRange( const CellRange
& rRange
)
370 static const sal_Unicode
aSpace( ' ' );
371 static const sal_Unicode
aQuote( '\'' );
373 ::rtl::OUStringBuffer aBuffer
;
375 if( (rRange
.aTableName
).getLength())
377 bool bNeedsEscaping
= ( rRange
.aTableName
.indexOf( aQuote
) > -1 );
378 bool bNeedsQuoting
= bNeedsEscaping
|| ( rRange
.aTableName
.indexOf( aSpace
) > -1 );
380 // quote table name if it contains spaces or quotes
384 aBuffer
.append( aQuote
);
386 // escape existing quotes
389 const sal_Unicode
* pTableNameBeg
= rRange
.aTableName
.getStr();
391 // append the quoted string at the buffer
392 ::std::for_each( pTableNameBeg
,
393 pTableNameBeg
+ rRange
.aTableName
.getLength(),
394 lcl_Escape( aBuffer
) );
397 aBuffer
.append( rRange
.aTableName
);
400 aBuffer
.append( aQuote
);
403 aBuffer
.append( rRange
.aTableName
);
405 aBuffer
.append( lcl_getXMLStringForCell( rRange
.aUpperLeft
));
407 if( ! rRange
.aLowerRight
.empty())
409 // we have a range (not a single cell)
410 aBuffer
.append( sal_Unicode( ':' ));
411 aBuffer
.append( lcl_getXMLStringForCell( rRange
.aLowerRight
));
414 return aBuffer
.makeStringAndClear();
417 } // namespace XMLRangeHelper