bump product version to 4.1.6.2
[LibreOffice.git] / chart2 / source / tools / XMLRangeHelper.cxx
blob387bfd0fa4c183628005122704d9c8e4557866d8
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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>
25 #include <algorithm>
26 #include <functional>
29 // ================================================================================
31 namespace
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 >
39 public:
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 );
52 private:
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 >
64 public:
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 );
74 private:
75 OUStringBuffer & m_aResultBuffer;
78 // ----------------------------------------
80 void lcl_getXMLStringForCell( const ::chart::XMLRangeHelper::Cell & rCell, OUStringBuffer * output )
82 OSL_ASSERT(output != 0);
84 if( rCell.empty())
85 return;
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
93 if( nCol < 26 )
94 output->append( (sal_Unicode)('A' + nCol) );
95 else if( nCol < 702 )
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 )
129 i--;
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 )
134 i--;
135 rOutCell.bRelativeRow = false;
137 else
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;
145 i--;
146 nPower *= 26;
148 rOutCell.nColumn = nColumn - 1;
150 rOutCell.bRelativeColumn = true;
151 if( i >= 0 &&
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;
171 // parse table name
172 while( nDelimiterPos < nEndPos &&
173 ( bInQuotation || rXMLString[ nDelimiterPos ] != aDot ))
175 // skip escaped characters (with backslash)
176 if( rXMLString[ nDelimiterPos ] == aBackslash )
177 ++nDelimiterPos;
178 // toggle quotation mode when finding single quotes
179 else if( rXMLString[ nDelimiterPos ] == aQuote )
180 bInQuotation = ! bInQuotation;
182 ++nDelimiterPos;
185 if( nDelimiterPos == -1 )
186 return false;
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 );
208 else
209 rOutTableName = aTableNameBuffer.makeStringAndClear();
211 else
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;
223 if( i==0 )
224 // only take first cell
225 lcl_getSingleCellAddressFromXMLString(
226 rXMLString, nDelimiterPos + 1, nNextDelimiterPos - 1, rOutCell );
229 return true;
232 bool lcl_getCellRangeAddressFromXMLString(
233 const OUString& rXMLString,
234 sal_Int32 nStartPos, sal_Int32 nEndPos,
235 ::chart::XMLRangeHelper::CellRange & rOutRange )
237 bool bResult = true;
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;
244 // parse table name
245 while( nDelimiterPos < nEndPos &&
246 ( bInQuotation || rXMLString[ nDelimiterPos ] != aColon ))
248 // skip escaped characters (with backslash)
249 if( rXMLString[ nDelimiterPos ] == aBackslash )
250 ++nDelimiterPos;
251 // toggle quotation mode when finding single quotes
252 else if( rXMLString[ nDelimiterPos ] == aQuote )
253 bInQuotation = ! bInQuotation;
255 ++nDelimiterPos;
258 if( nDelimiterPos == nEndPos )
260 // only one cell
261 bResult = lcl_getCellAddressFromXMLString( rXMLString, nStartPos, nEndPos,
262 rOutRange.aUpperLeft,
263 rOutRange.aTableName );
264 if( rOutRange.aTableName.isEmpty() )
265 bResult = false;
267 else
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() )
274 bResult = false;
276 OUString sTableSecondName;
277 if( bResult )
279 bResult = lcl_getCellAddressFromXMLString( rXMLString, nDelimiterPos + 1, nEndPos,
280 rOutRange.aLowerRight,
281 sTableSecondName );
283 if( bResult &&
284 !sTableSecondName.isEmpty() &&
285 ! sTableSecondName.equals( rOutRange.aTableName ))
286 bResult = false;
289 return bResult;
292 } // anonymous namespace
294 // ================================================================================
296 namespace chart
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();
313 // reset
314 CellRange aResult;
316 // iterate over different ranges
317 for( sal_Int32 i = 0;
318 nEndPos < nLength;
319 nStartPos = ++nEndPos, i++ )
321 // find start point of next range
323 // ignore leading '$'
324 if( rXMLString[ nEndPos ] == aDollar)
325 nEndPos++;
327 bool bInQuotation = false;
328 // parse range
329 while( nEndPos < nLength &&
330 ( bInQuotation || rXMLString[ nEndPos ] != aSpace ))
332 // skip escaped characters (with backslash)
333 if( rXMLString[ nEndPos ] == aBackslash )
334 ++nEndPos;
335 // toggle quotation mode when finding single quotes
336 else if( rXMLString[ nEndPos ] == aQuote )
337 bInQuotation = ! bInQuotation;
339 ++nEndPos;
342 if( ! lcl_getCellRangeAddressFromXMLString(
343 rXMLString,
344 nStartPos, nEndPos - 1,
345 aResult ))
347 // if an error occurred, bail out
348 return CellRange();
352 return aResult;
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
368 if( bNeedsQuoting )
370 // leading quote
371 aBuffer.append( aQuote );
373 // escape existing quotes
374 if( bNeedsEscaping )
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 ) );
383 else
384 aBuffer.append( rRange.aTableName );
386 // final quote
387 aBuffer.append( aQuote );
389 else
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
405 } // namespace chart
407 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */