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_sw.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 ||
202 nDelimiterPos
>= nEndPos
)
206 if( nDelimiterPos
> nStartPos
)
208 // there is a table name before the address
210 ::rtl::OUStringBuffer aTableNameBuffer
;
211 const sal_Unicode
* pTableName
= rXMLString
.getStr();
213 // remove escapes from table name
214 ::std::for_each( pTableName
+ nStartPos
,
215 pTableName
+ nDelimiterPos
,
216 lcl_UnEscape( aTableNameBuffer
));
218 // unquote quoted table name
219 const sal_Unicode
* pBuf
= aTableNameBuffer
.getStr();
220 if( pBuf
[ 0 ] == aQuote
&&
221 pBuf
[ aTableNameBuffer
.getLength() - 1 ] == aQuote
)
223 ::rtl::OUString aName
= aTableNameBuffer
.makeStringAndClear();
224 rOutTableName
= aName
.copy( 1, aName
.getLength() - 2 );
227 rOutTableName
= aTableNameBuffer
.makeStringAndClear();
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
);
283 // range (separated by a colon)
284 bResult
= lcl_getCellAddressFromXMLString( rXMLString
, nStartPos
, nDelimiterPos
- 1,
285 rOutRange
.aUpperLeft
,
286 rOutRange
.aTableName
);
287 ::rtl::OUString sTableSecondName
;
290 bResult
= lcl_getCellAddressFromXMLString( rXMLString
, nDelimiterPos
+ 1, nEndPos
,
291 rOutRange
.aLowerRight
,
295 sTableSecondName
.getLength() &&
296 ! sTableSecondName
.equals( rOutRange
.aTableName
))
303 } // anonymous namespace
305 // ================================================================================
309 namespace XMLRangeHelper
312 CellRange
getCellRangeFromXMLString( const OUString
& rXMLString
)
314 static const sal_Unicode
aSpace( ' ' );
315 static const sal_Unicode
aQuote( '\'' );
316 static const sal_Unicode
aDollar( '$' );
317 static const sal_Unicode
aBackslash( '\\' );
319 sal_Int32 nStartPos
= 0;
320 sal_Int32 nEndPos
= nStartPos
;
321 const sal_Int32 nLength
= rXMLString
.getLength();
326 // iterate over different ranges
327 for( sal_Int32 i
= 0;
329 nStartPos
= ++nEndPos
, i
++ )
331 // find start point of next range
333 // ignore leading '$'
334 if( rXMLString
[ nEndPos
] == aDollar
)
337 bool bInQuotation
= false;
339 while( nEndPos
< nLength
&&
340 ( bInQuotation
|| rXMLString
[ nEndPos
] != aSpace
))
342 // skip escaped characters (with backslash)
343 if( rXMLString
[ nEndPos
] == aBackslash
)
345 // toggle quotation mode when finding single quotes
346 else if( rXMLString
[ nEndPos
] == aQuote
)
347 bInQuotation
= ! bInQuotation
;
352 if( ! lcl_getCellRangeAddressFromXMLString(
354 nStartPos
, nEndPos
- 1,
357 // if an error occured, bail out
365 OUString
getXMLStringFromCellRange( const CellRange
& rRange
)
367 static const sal_Unicode
aSpace( ' ' );
368 static const sal_Unicode
aQuote( '\'' );
370 ::rtl::OUStringBuffer aBuffer
;
372 if( (rRange
.aTableName
).getLength())
374 bool bNeedsEscaping
= ( rRange
.aTableName
.indexOf( aQuote
) > -1 );
375 bool bNeedsQuoting
= bNeedsEscaping
|| ( rRange
.aTableName
.indexOf( aSpace
) > -1 );
377 // quote table name if it contains spaces or quotes
381 aBuffer
.append( aQuote
);
383 // escape existing quotes
386 const sal_Unicode
* pTableNameBeg
= rRange
.aTableName
.getStr();
388 // append the quoted string at the buffer
389 ::std::for_each( pTableNameBeg
,
390 pTableNameBeg
+ rRange
.aTableName
.getLength(),
391 lcl_Escape( aBuffer
) );
394 aBuffer
.append( rRange
.aTableName
);
397 aBuffer
.append( aQuote
);
400 aBuffer
.append( rRange
.aTableName
);
402 aBuffer
.append( lcl_getXMLStringForCell( rRange
.aUpperLeft
));
404 if( ! rRange
.aLowerRight
.empty())
406 // we have a range (not a single cell)
407 aBuffer
.append( sal_Unicode( ':' ));
408 aBuffer
.append( lcl_getXMLStringForCell( rRange
.aLowerRight
));
411 return aBuffer
.makeStringAndClear();
414 } // namespace XMLRangeHelper
415 //} // namespace chart