merge the formfield patch from ooo-build
[ooovba.git] / chart2 / source / tools / XMLRangeHelper.cxx
blobfe4f8255892cd71aa8dd114958035eb6fc350386
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: XMLRangeHelper.cxx,v $
10 * $Revision: 1.4 $
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>
38 #include <algorithm>
39 #include <functional>
41 using ::rtl::OUString;
42 using ::rtl::OUStringBuffer;
44 // ================================================================================
46 namespace
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 >
54 public:
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 );
67 private:
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 >
79 public:
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 );
89 private:
90 ::rtl::OUStringBuffer & m_aResultBuffer;
93 // ----------------------------------------
95 OUStringBuffer lcl_getXMLStringForCell( const ::chart::XMLRangeHelper::Cell & rCell )
97 ::rtl::OUStringBuffer aBuffer;
98 if( rCell.empty())
99 return 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
107 if( nCol < 26 )
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 );
126 return aBuffer;
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 )
145 i--;
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 )
150 i--;
151 rOutCell.bRelativeRow = false;
153 else
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;
161 i--;
162 nPower *= 26;
164 rOutCell.nColumn = nColumn - 1;
166 rOutCell.bRelativeColumn = true;
167 if( i >= 0 &&
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;
187 // parse table name
188 while( nDelimiterPos < nEndPos &&
189 ( bInQuotation || rXMLString[ nDelimiterPos ] != aDot ))
191 // skip escaped characters (with backslash)
192 if( rXMLString[ nDelimiterPos ] == aBackslash )
193 ++nDelimiterPos;
194 // toggle quotation mode when finding single quotes
195 else if( rXMLString[ nDelimiterPos ] == aQuote )
196 bInQuotation = ! bInQuotation;
198 ++nDelimiterPos;
201 if( nDelimiterPos == -1 )
202 return false;
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 );
224 else
225 rOutTableName = aTableNameBuffer.makeStringAndClear();
227 else
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;
239 if( i==0 )
240 // only take first cell
241 lcl_getSingleCellAddressFromXMLString(
242 rXMLString, nDelimiterPos + 1, nNextDelimiterPos - 1, rOutCell );
245 return true;
248 bool lcl_getCellRangeAddressFromXMLString(
249 const ::rtl::OUString& rXMLString,
250 sal_Int32 nStartPos, sal_Int32 nEndPos,
251 ::chart::XMLRangeHelper::CellRange & rOutRange )
253 bool bResult = true;
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;
260 // parse table name
261 while( nDelimiterPos < nEndPos &&
262 ( bInQuotation || rXMLString[ nDelimiterPos ] != aColon ))
264 // skip escaped characters (with backslash)
265 if( rXMLString[ nDelimiterPos ] == aBackslash )
266 ++nDelimiterPos;
267 // toggle quotation mode when finding single quotes
268 else if( rXMLString[ nDelimiterPos ] == aQuote )
269 bInQuotation = ! bInQuotation;
271 ++nDelimiterPos;
274 if( nDelimiterPos == nEndPos )
276 // only one cell
277 bResult = lcl_getCellAddressFromXMLString( rXMLString, nStartPos, nEndPos,
278 rOutRange.aUpperLeft,
279 rOutRange.aTableName );
280 if( !rOutRange.aTableName.getLength() )
281 bResult = false;
283 else
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() )
290 bResult = false;
292 ::rtl::OUString sTableSecondName;
293 if( bResult )
295 bResult = lcl_getCellAddressFromXMLString( rXMLString, nDelimiterPos + 1, nEndPos,
296 rOutRange.aLowerRight,
297 sTableSecondName );
299 if( bResult &&
300 sTableSecondName.getLength() &&
301 ! sTableSecondName.equals( rOutRange.aTableName ))
302 bResult = false;
305 return bResult;
308 } // anonymous namespace
310 // ================================================================================
312 namespace chart
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();
329 // reset
330 CellRange aResult;
332 // iterate over different ranges
333 for( sal_Int32 i = 0;
334 nEndPos < nLength;
335 nStartPos = ++nEndPos, i++ )
337 // find start point of next range
339 // ignore leading '$'
340 if( rXMLString[ nEndPos ] == aDollar)
341 nEndPos++;
343 bool bInQuotation = false;
344 // parse range
345 while( nEndPos < nLength &&
346 ( bInQuotation || rXMLString[ nEndPos ] != aSpace ))
348 // skip escaped characters (with backslash)
349 if( rXMLString[ nEndPos ] == aBackslash )
350 ++nEndPos;
351 // toggle quotation mode when finding single quotes
352 else if( rXMLString[ nEndPos ] == aQuote )
353 bInQuotation = ! bInQuotation;
355 ++nEndPos;
358 if( ! lcl_getCellRangeAddressFromXMLString(
359 rXMLString,
360 nStartPos, nEndPos - 1,
361 aResult ))
363 // if an error occured, bail out
364 return CellRange();
368 return aResult;
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
384 if( bNeedsQuoting )
386 // leading quote
387 aBuffer.append( aQuote );
389 // escape existing quotes
390 if( bNeedsEscaping )
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 ) );
399 else
400 aBuffer.append( rRange.aTableName );
402 // final quote
403 aBuffer.append( aQuote );
405 else
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
421 } // namespace chart