1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: XMLRangeHelper.cxx,v $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_chart2.hxx"
34 #include "XMLRangeHelper.hxx"
35 #include <unotools/charclass.hxx>
36 #include <rtl/ustrbuf.hxx>
41 using ::rtl::OUString
;
42 using ::rtl::OUStringBuffer
;
44 // ================================================================================
48 /** unary function that escapes backslashes and single quotes in a sal_Unicode
49 array (which you can get from an OUString with getStr()) and puts the result
50 into the OUStringBuffer given in the CTOR
52 class lcl_Escape
: public ::std::unary_function
< sal_Unicode
, void >
55 lcl_Escape( ::rtl::OUStringBuffer
& aResultBuffer
) : m_aResultBuffer( aResultBuffer
) {}
56 void operator() ( sal_Unicode aChar
)
58 static const sal_Unicode
m_aQuote( '\'' );
59 static const sal_Unicode
m_aBackslash( '\\' );
61 if( aChar
== m_aQuote
||
62 aChar
== m_aBackslash
)
63 m_aResultBuffer
.append( m_aBackslash
);
64 m_aResultBuffer
.append( aChar
);
68 ::rtl::OUStringBuffer
& m_aResultBuffer
;
71 // ----------------------------------------
73 /** unary function that removes backslash escapes in a sal_Unicode array (which
74 you can get from an OUString with getStr()) and puts the result into the
75 OUStringBuffer given in the CTOR
77 class lcl_UnEscape
: public ::std::unary_function
< sal_Unicode
, void >
80 lcl_UnEscape( ::rtl::OUStringBuffer
& aResultBuffer
) : m_aResultBuffer( aResultBuffer
) {}
81 void operator() ( sal_Unicode aChar
)
83 static const sal_Unicode
m_aBackslash( '\\' );
85 if( aChar
!= m_aBackslash
)
86 m_aResultBuffer
.append( aChar
);
90 ::rtl::OUStringBuffer
& m_aResultBuffer
;
93 // ----------------------------------------
95 OUStringBuffer
lcl_getXMLStringForCell( const ::chart::XMLRangeHelper::Cell
& rCell
)
97 ::rtl::OUStringBuffer aBuffer
;
101 sal_Int32 nCol
= rCell
.nColumn
;
102 aBuffer
.append( (sal_Unicode
)'.' );
103 if( ! rCell
.bRelativeColumn
)
104 aBuffer
.append( (sal_Unicode
)'$' );
106 // get A, B, C, ..., AA, AB, ... representation of column number
108 aBuffer
.append( (sal_Unicode
)('A' + nCol
) );
109 else if( nCol
< 702 )
111 aBuffer
.append( (sal_Unicode
)('A' + nCol
/ 26 - 1 ));
112 aBuffer
.append( (sal_Unicode
)('A' + nCol
% 26) );
114 else // works for nCol <= 18,278
116 aBuffer
.append( (sal_Unicode
)('A' + nCol
/ 702 - 1 ));
117 aBuffer
.append( (sal_Unicode
)('A' + (nCol
% 702) / 26 ));
118 aBuffer
.append( (sal_Unicode
)('A' + nCol
% 26) );
121 // write row number as number
122 if( ! rCell
.bRelativeRow
)
123 aBuffer
.append( (sal_Unicode
)'$' );
124 aBuffer
.append( rCell
.nRow
+ (sal_Int32
)1 );
129 void lcl_getSingleCellAddressFromXMLString(
130 const ::rtl::OUString
& rXMLString
,
131 sal_Int32 nStartPos
, sal_Int32 nEndPos
,
132 ::chart::XMLRangeHelper::Cell
& rOutCell
)
134 // expect "\$?[a-zA-Z]+\$?[1-9][0-9]*"
135 static const sal_Unicode
aDollar( '$' );
136 static const sal_Unicode
aLetterA( 'A' );
138 ::rtl::OUString aCellStr
= rXMLString
.copy( nStartPos
, nEndPos
- nStartPos
+ 1 ).toAsciiUpperCase();
139 const sal_Unicode
* pStrArray
= aCellStr
.getStr();
140 sal_Int32 nLength
= aCellStr
.getLength();
141 sal_Int32 i
= nLength
- 1, nColumn
= 0;
143 // parse number for row
144 while( CharClass::isAsciiDigit( pStrArray
[ i
] ) && i
>= 0 )
146 rOutCell
.nRow
= (aCellStr
.copy( i
+ 1 )).toInt32() - 1;
147 // a dollar in XML means absolute (whereas in UI it means relative)
148 if( pStrArray
[ i
] == aDollar
)
151 rOutCell
.bRelativeRow
= false;
154 rOutCell
.bRelativeRow
= true;
156 // parse rest for column
157 sal_Int32 nPower
= 1;
158 while( CharClass::isAsciiAlpha( pStrArray
[ i
] ))
160 nColumn
+= (pStrArray
[ i
] - aLetterA
+ 1) * nPower
;
164 rOutCell
.nColumn
= nColumn
- 1;
166 rOutCell
.bRelativeColumn
= true;
168 pStrArray
[ i
] == aDollar
)
169 rOutCell
.bRelativeColumn
= false;
170 rOutCell
.bIsEmpty
= false;
173 bool lcl_getCellAddressFromXMLString(
174 const ::rtl::OUString
& rXMLString
,
175 sal_Int32 nStartPos
, sal_Int32 nEndPos
,
176 ::chart::XMLRangeHelper::Cell
& rOutCell
,
177 ::rtl::OUString
& rOutTableName
)
179 static const sal_Unicode
aDot( '.' );
180 static const sal_Unicode
aQuote( '\'' );
181 static const sal_Unicode
aBackslash( '\\' );
183 sal_Int32 nNextDelimiterPos
= nStartPos
;
185 sal_Int32 nDelimiterPos
= nStartPos
;
186 bool bInQuotation
= false;
188 while( nDelimiterPos
< nEndPos
&&
189 ( bInQuotation
|| rXMLString
[ nDelimiterPos
] != aDot
))
191 // skip escaped characters (with backslash)
192 if( rXMLString
[ nDelimiterPos
] == aBackslash
)
194 // toggle quotation mode when finding single quotes
195 else if( rXMLString
[ nDelimiterPos
] == aQuote
)
196 bInQuotation
= ! bInQuotation
;
201 if( nDelimiterPos
== -1 )
204 if( nDelimiterPos
> nStartPos
&& nDelimiterPos
< nEndPos
)
206 // there is a table name before the address
208 ::rtl::OUStringBuffer aTableNameBuffer
;
209 const sal_Unicode
* pTableName
= rXMLString
.getStr();
211 // remove escapes from table name
212 ::std::for_each( pTableName
+ nStartPos
,
213 pTableName
+ nDelimiterPos
,
214 lcl_UnEscape( aTableNameBuffer
));
216 // unquote quoted table name
217 const sal_Unicode
* pBuf
= aTableNameBuffer
.getStr();
218 if( pBuf
[ 0 ] == aQuote
&&
219 pBuf
[ aTableNameBuffer
.getLength() - 1 ] == aQuote
)
221 ::rtl::OUString aName
= aTableNameBuffer
.makeStringAndClear();
222 rOutTableName
= aName
.copy( 1, aName
.getLength() - 2 );
225 rOutTableName
= aTableNameBuffer
.makeStringAndClear();
228 nDelimiterPos
= nStartPos
;
230 for( sal_Int32 i
= 0;
231 nNextDelimiterPos
< nEndPos
;
232 nDelimiterPos
= nNextDelimiterPos
, i
++ )
234 nNextDelimiterPos
= rXMLString
.indexOf( aDot
, nDelimiterPos
+ 1 );
235 if( nNextDelimiterPos
== -1 ||
236 nNextDelimiterPos
> nEndPos
)
237 nNextDelimiterPos
= nEndPos
+ 1;
240 // only take first cell
241 lcl_getSingleCellAddressFromXMLString(
242 rXMLString
, nDelimiterPos
+ 1, nNextDelimiterPos
- 1, rOutCell
);
248 bool lcl_getCellRangeAddressFromXMLString(
249 const ::rtl::OUString
& rXMLString
,
250 sal_Int32 nStartPos
, sal_Int32 nEndPos
,
251 ::chart::XMLRangeHelper::CellRange
& rOutRange
)
254 static const sal_Unicode
aColon( ':' );
255 static const sal_Unicode
aQuote( '\'' );
256 static const sal_Unicode
aBackslash( '\\' );
258 sal_Int32 nDelimiterPos
= nStartPos
;
259 bool bInQuotation
= false;
261 while( nDelimiterPos
< nEndPos
&&
262 ( bInQuotation
|| rXMLString
[ nDelimiterPos
] != aColon
))
264 // skip escaped characters (with backslash)
265 if( rXMLString
[ nDelimiterPos
] == aBackslash
)
267 // toggle quotation mode when finding single quotes
268 else if( rXMLString
[ nDelimiterPos
] == aQuote
)
269 bInQuotation
= ! bInQuotation
;
274 if( nDelimiterPos
== nEndPos
)
277 bResult
= lcl_getCellAddressFromXMLString( rXMLString
, nStartPos
, nEndPos
,
278 rOutRange
.aUpperLeft
,
279 rOutRange
.aTableName
);
280 if( !rOutRange
.aTableName
.getLength() )
285 // range (separated by a colon)
286 bResult
= lcl_getCellAddressFromXMLString( rXMLString
, nStartPos
, nDelimiterPos
- 1,
287 rOutRange
.aUpperLeft
,
288 rOutRange
.aTableName
);
289 if( !rOutRange
.aTableName
.getLength() )
292 ::rtl::OUString sTableSecondName
;
295 bResult
= lcl_getCellAddressFromXMLString( rXMLString
, nDelimiterPos
+ 1, nEndPos
,
296 rOutRange
.aLowerRight
,
300 sTableSecondName
.getLength() &&
301 ! sTableSecondName
.equals( rOutRange
.aTableName
))
308 } // anonymous namespace
310 // ================================================================================
314 namespace XMLRangeHelper
317 CellRange
getCellRangeFromXMLString( const OUString
& rXMLString
)
319 static const sal_Unicode
aSpace( ' ' );
320 static const sal_Unicode
aQuote( '\'' );
321 // static const sal_Unicode aDoubleQuote( '\"' );
322 static const sal_Unicode
aDollar( '$' );
323 static const sal_Unicode
aBackslash( '\\' );
325 sal_Int32 nStartPos
= 0;
326 sal_Int32 nEndPos
= nStartPos
;
327 const sal_Int32 nLength
= rXMLString
.getLength();
332 // iterate over different ranges
333 for( sal_Int32 i
= 0;
335 nStartPos
= ++nEndPos
, i
++ )
337 // find start point of next range
339 // ignore leading '$'
340 if( rXMLString
[ nEndPos
] == aDollar
)
343 bool bInQuotation
= false;
345 while( nEndPos
< nLength
&&
346 ( bInQuotation
|| rXMLString
[ nEndPos
] != aSpace
))
348 // skip escaped characters (with backslash)
349 if( rXMLString
[ nEndPos
] == aBackslash
)
351 // toggle quotation mode when finding single quotes
352 else if( rXMLString
[ nEndPos
] == aQuote
)
353 bInQuotation
= ! bInQuotation
;
358 if( ! lcl_getCellRangeAddressFromXMLString(
360 nStartPos
, nEndPos
- 1,
363 // if an error occured, bail out
371 OUString
getXMLStringFromCellRange( const CellRange
& rRange
)
373 static const sal_Unicode
aSpace( ' ' );
374 static const sal_Unicode
aQuote( '\'' );
376 ::rtl::OUStringBuffer aBuffer
;
378 if( (rRange
.aTableName
).getLength())
380 bool bNeedsEscaping
= ( rRange
.aTableName
.indexOf( aQuote
) > -1 );
381 bool bNeedsQuoting
= bNeedsEscaping
|| ( rRange
.aTableName
.indexOf( aSpace
) > -1 );
383 // quote table name if it contains spaces or quotes
387 aBuffer
.append( aQuote
);
389 // escape existing quotes
392 const sal_Unicode
* pTableNameBeg
= rRange
.aTableName
.getStr();
394 // append the quoted string at the buffer
395 ::std::for_each( pTableNameBeg
,
396 pTableNameBeg
+ rRange
.aTableName
.getLength(),
397 lcl_Escape( aBuffer
) );
400 aBuffer
.append( rRange
.aTableName
);
403 aBuffer
.append( aQuote
);
406 aBuffer
.append( rRange
.aTableName
);
408 aBuffer
.append( lcl_getXMLStringForCell( rRange
.aUpperLeft
));
410 if( ! rRange
.aLowerRight
.empty())
412 // we have a range (not a single cell)
413 aBuffer
.append( sal_Unicode( ':' ));
414 aBuffer
.append( lcl_getXMLStringForCell( rRange
.aLowerRight
));
417 return aBuffer
.makeStringAndClear();
420 } // namespace XMLRangeHelper