merged tag ooo/OOO330_m14
[LibreOffice.git] / chart2 / source / tools / XMLRangeHelper.cxx
blobc5d345db01245ca3e92bb7b8a0e8feebf367d161
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>
35 #include <algorithm>
36 #include <functional>
38 using ::rtl::OUString;
39 using ::rtl::OUStringBuffer;
41 // ================================================================================
43 namespace
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 >
51 public:
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 );
64 private:
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 >
76 public:
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 );
86 private:
87 ::rtl::OUStringBuffer & m_aResultBuffer;
90 // ----------------------------------------
92 OUStringBuffer lcl_getXMLStringForCell( const ::chart::XMLRangeHelper::Cell & rCell )
94 ::rtl::OUStringBuffer aBuffer;
95 if( rCell.empty())
96 return 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
104 if( nCol < 26 )
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 );
123 return aBuffer;
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 )
142 i--;
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 )
147 i--;
148 rOutCell.bRelativeRow = false;
150 else
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;
158 i--;
159 nPower *= 26;
161 rOutCell.nColumn = nColumn - 1;
163 rOutCell.bRelativeColumn = true;
164 if( i >= 0 &&
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;
184 // parse table name
185 while( nDelimiterPos < nEndPos &&
186 ( bInQuotation || rXMLString[ nDelimiterPos ] != aDot ))
188 // skip escaped characters (with backslash)
189 if( rXMLString[ nDelimiterPos ] == aBackslash )
190 ++nDelimiterPos;
191 // toggle quotation mode when finding single quotes
192 else if( rXMLString[ nDelimiterPos ] == aQuote )
193 bInQuotation = ! bInQuotation;
195 ++nDelimiterPos;
198 if( nDelimiterPos == -1 )
199 return false;
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 );
221 else
222 rOutTableName = aTableNameBuffer.makeStringAndClear();
224 else
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;
236 if( i==0 )
237 // only take first cell
238 lcl_getSingleCellAddressFromXMLString(
239 rXMLString, nDelimiterPos + 1, nNextDelimiterPos - 1, rOutCell );
242 return true;
245 bool lcl_getCellRangeAddressFromXMLString(
246 const ::rtl::OUString& rXMLString,
247 sal_Int32 nStartPos, sal_Int32 nEndPos,
248 ::chart::XMLRangeHelper::CellRange & rOutRange )
250 bool bResult = true;
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;
257 // parse table name
258 while( nDelimiterPos < nEndPos &&
259 ( bInQuotation || rXMLString[ nDelimiterPos ] != aColon ))
261 // skip escaped characters (with backslash)
262 if( rXMLString[ nDelimiterPos ] == aBackslash )
263 ++nDelimiterPos;
264 // toggle quotation mode when finding single quotes
265 else if( rXMLString[ nDelimiterPos ] == aQuote )
266 bInQuotation = ! bInQuotation;
268 ++nDelimiterPos;
271 if( nDelimiterPos == nEndPos )
273 // only one cell
274 bResult = lcl_getCellAddressFromXMLString( rXMLString, nStartPos, nEndPos,
275 rOutRange.aUpperLeft,
276 rOutRange.aTableName );
277 if( !rOutRange.aTableName.getLength() )
278 bResult = false;
280 else
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() )
287 bResult = false;
289 ::rtl::OUString sTableSecondName;
290 if( bResult )
292 bResult = lcl_getCellAddressFromXMLString( rXMLString, nDelimiterPos + 1, nEndPos,
293 rOutRange.aLowerRight,
294 sTableSecondName );
296 if( bResult &&
297 sTableSecondName.getLength() &&
298 ! sTableSecondName.equals( rOutRange.aTableName ))
299 bResult = false;
302 return bResult;
305 } // anonymous namespace
307 // ================================================================================
309 namespace chart
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();
326 // reset
327 CellRange aResult;
329 // iterate over different ranges
330 for( sal_Int32 i = 0;
331 nEndPos < nLength;
332 nStartPos = ++nEndPos, i++ )
334 // find start point of next range
336 // ignore leading '$'
337 if( rXMLString[ nEndPos ] == aDollar)
338 nEndPos++;
340 bool bInQuotation = false;
341 // parse range
342 while( nEndPos < nLength &&
343 ( bInQuotation || rXMLString[ nEndPos ] != aSpace ))
345 // skip escaped characters (with backslash)
346 if( rXMLString[ nEndPos ] == aBackslash )
347 ++nEndPos;
348 // toggle quotation mode when finding single quotes
349 else if( rXMLString[ nEndPos ] == aQuote )
350 bInQuotation = ! bInQuotation;
352 ++nEndPos;
355 if( ! lcl_getCellRangeAddressFromXMLString(
356 rXMLString,
357 nStartPos, nEndPos - 1,
358 aResult ))
360 // if an error occured, bail out
361 return CellRange();
365 return aResult;
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
381 if( bNeedsQuoting )
383 // leading quote
384 aBuffer.append( aQuote );
386 // escape existing quotes
387 if( bNeedsEscaping )
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 ) );
396 else
397 aBuffer.append( rRange.aTableName );
399 // final quote
400 aBuffer.append( aQuote );
402 else
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
418 } // namespace chart