Branch data Line data Source code
1 : : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : : /*
3 : : * This file is part of the LibreOffice project.
4 : : *
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/.
8 : : *
9 : : * This file incorporates work covered by the following license notice:
10 : : *
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 .
18 : : */
19 : :
20 : :
21 : : #include "XMLRangeHelper.hxx"
22 : : #include <unotools/charclass.hxx>
23 : : #include <rtl/ustrbuf.hxx>
24 : :
25 : : #include <algorithm>
26 : : #include <functional>
27 : :
28 : : using ::rtl::OUString;
29 : : using ::rtl::OUStringBuffer;
30 : :
31 : : // ================================================================================
32 : :
33 : : namespace
34 : : {
35 : : /** unary function that escapes backslashes and single quotes in a sal_Unicode
36 : : array (which you can get from an OUString with getStr()) and puts the result
37 : : into the OUStringBuffer given in the CTOR
38 : : */
39 : : class lcl_Escape : public ::std::unary_function< sal_Unicode, void >
40 : : {
41 : : public:
42 : 0 : lcl_Escape( ::rtl::OUStringBuffer & aResultBuffer ) : m_aResultBuffer( aResultBuffer ) {}
43 : 0 : void operator() ( sal_Unicode aChar )
44 : : {
45 : : static const sal_Unicode m_aQuote( '\'' );
46 : : static const sal_Unicode m_aBackslash( '\\' );
47 : :
48 [ # # ][ # # ]: 0 : if( aChar == m_aQuote ||
49 : : aChar == m_aBackslash )
50 : 0 : m_aResultBuffer.append( m_aBackslash );
51 : 0 : m_aResultBuffer.append( aChar );
52 : 0 : }
53 : :
54 : : private:
55 : : ::rtl::OUStringBuffer & m_aResultBuffer;
56 : : };
57 : :
58 : : // ----------------------------------------
59 : :
60 : : /** unary function that removes backslash escapes in a sal_Unicode array (which
61 : : you can get from an OUString with getStr()) and puts the result into the
62 : : OUStringBuffer given in the CTOR
63 : : */
64 : : class lcl_UnEscape : public ::std::unary_function< sal_Unicode, void >
65 : : {
66 : : public:
67 : 42 : lcl_UnEscape( ::rtl::OUStringBuffer & aResultBuffer ) : m_aResultBuffer( aResultBuffer ) {}
68 : 462 : void operator() ( sal_Unicode aChar )
69 : : {
70 : : static const sal_Unicode m_aBackslash( '\\' );
71 : :
72 [ + - ]: 462 : if( aChar != m_aBackslash )
73 : 462 : m_aResultBuffer.append( aChar );
74 : 462 : }
75 : :
76 : : private:
77 : : ::rtl::OUStringBuffer & m_aResultBuffer;
78 : : };
79 : :
80 : : // ----------------------------------------
81 : :
82 : 224 : void lcl_getXMLStringForCell( const ::chart::XMLRangeHelper::Cell & rCell, rtl::OUStringBuffer * output )
83 : : {
84 : : OSL_ASSERT(output != 0);
85 : :
86 [ - + ]: 224 : if( rCell.empty())
87 : 224 : return;
88 : :
89 : 224 : sal_Int32 nCol = rCell.nColumn;
90 : 224 : output->append( (sal_Unicode)'.' );
91 [ + - ]: 224 : if( ! rCell.bRelativeColumn )
92 : 224 : output->append( (sal_Unicode)'$' );
93 : :
94 : : // get A, B, C, ..., AA, AB, ... representation of column number
95 [ + - ]: 224 : if( nCol < 26 )
96 : 224 : output->append( (sal_Unicode)('A' + nCol) );
97 [ # # ]: 0 : else if( nCol < 702 )
98 : : {
99 : 0 : output->append( (sal_Unicode)('A' + nCol / 26 - 1 ));
100 : 0 : output->append( (sal_Unicode)('A' + nCol % 26) );
101 : : }
102 : : else // works for nCol <= 18,278
103 : : {
104 : 0 : output->append( (sal_Unicode)('A' + nCol / 702 - 1 ));
105 : 0 : output->append( (sal_Unicode)('A' + (nCol % 702) / 26 ));
106 : 0 : output->append( (sal_Unicode)('A' + nCol % 26) );
107 : : }
108 : :
109 : : // write row number as number
110 [ + - ]: 224 : if( ! rCell.bRelativeRow )
111 : 224 : output->append( (sal_Unicode)'$' );
112 : 224 : output->append( rCell.nRow + (sal_Int32)1 );
113 : : }
114 : :
115 : 66 : void lcl_getSingleCellAddressFromXMLString(
116 : : const ::rtl::OUString& rXMLString,
117 : : sal_Int32 nStartPos, sal_Int32 nEndPos,
118 : : ::chart::XMLRangeHelper::Cell & rOutCell )
119 : : {
120 : : // expect "\$?[a-zA-Z]+\$?[1-9][0-9]*"
121 : : static const sal_Unicode aDollar( '$' );
122 : : static const sal_Unicode aLetterA( 'A' );
123 : :
124 : 66 : ::rtl::OUString aCellStr = rXMLString.copy( nStartPos, nEndPos - nStartPos + 1 ).toAsciiUpperCase();
125 : 66 : const sal_Unicode* pStrArray = aCellStr.getStr();
126 : 66 : sal_Int32 nLength = aCellStr.getLength();
127 : 66 : sal_Int32 i = nLength - 1, nColumn = 0;
128 : :
129 : : // parse number for row
130 [ + + ][ + - ]: 132 : while( CharClass::isAsciiDigit( pStrArray[ i ] ) && i >= 0 )
[ + + ]
131 : 66 : i--;
132 : 66 : rOutCell.nRow = (aCellStr.copy( i + 1 )).toInt32() - 1;
133 : : // a dollar in XML means absolute (whereas in UI it means relative)
134 [ - + ]: 66 : if( pStrArray[ i ] == aDollar )
135 : : {
136 : 0 : i--;
137 : 0 : rOutCell.bRelativeRow = false;
138 : : }
139 : : else
140 : 66 : rOutCell.bRelativeRow = true;
141 : :
142 : : // parse rest for column
143 : 66 : sal_Int32 nPower = 1;
144 [ + + ]: 132 : while( CharClass::isAsciiAlpha( pStrArray[ i ] ))
145 : : {
146 : 66 : nColumn += (pStrArray[ i ] - aLetterA + 1) * nPower;
147 : 66 : i--;
148 : 66 : nPower *= 26;
149 : : }
150 : 66 : rOutCell.nColumn = nColumn - 1;
151 : :
152 : 66 : rOutCell.bRelativeColumn = true;
153 [ - + ][ # # ]: 66 : if( i >= 0 &&
154 : 0 : pStrArray[ i ] == aDollar )
155 : 0 : rOutCell.bRelativeColumn = false;
156 : 66 : rOutCell.bIsEmpty = false;
157 : 66 : }
158 : :
159 : 66 : bool lcl_getCellAddressFromXMLString(
160 : : const ::rtl::OUString& rXMLString,
161 : : sal_Int32 nStartPos, sal_Int32 nEndPos,
162 : : ::chart::XMLRangeHelper::Cell & rOutCell,
163 : : ::rtl::OUString& rOutTableName )
164 : : {
165 : : static const sal_Unicode aDot( '.' );
166 : : static const sal_Unicode aQuote( '\'' );
167 : : static const sal_Unicode aBackslash( '\\' );
168 : :
169 : 66 : sal_Int32 nNextDelimiterPos = nStartPos;
170 : :
171 : 66 : sal_Int32 nDelimiterPos = nStartPos;
172 : 66 : bool bInQuotation = false;
173 : : // parse table name
174 [ + - ]: 1056 : while( nDelimiterPos < nEndPos &&
[ + - + + ]
[ + + ]
175 : 528 : ( bInQuotation || rXMLString[ nDelimiterPos ] != aDot ))
176 : : {
177 : : // skip escaped characters (with backslash)
178 [ - + ]: 462 : if( rXMLString[ nDelimiterPos ] == aBackslash )
179 : 0 : ++nDelimiterPos;
180 : : // toggle quotation mode when finding single quotes
181 [ - + ]: 462 : else if( rXMLString[ nDelimiterPos ] == aQuote )
182 : 0 : bInQuotation = ! bInQuotation;
183 : :
184 : 462 : ++nDelimiterPos;
185 : : }
186 : :
187 [ - + ]: 66 : if( nDelimiterPos == -1 )
188 : 0 : return false;
189 : :
190 [ + + ][ + - ]: 66 : if( nDelimiterPos > nStartPos && nDelimiterPos < nEndPos )
191 : : {
192 : : // there is a table name before the address
193 : :
194 : 42 : ::rtl::OUStringBuffer aTableNameBuffer;
195 : 42 : const sal_Unicode * pTableName = rXMLString.getStr();
196 : :
197 : : // remove escapes from table name
198 : : ::std::for_each( pTableName + nStartPos,
199 : : pTableName + nDelimiterPos,
200 [ + - ]: 42 : lcl_UnEscape( aTableNameBuffer ));
201 : :
202 : : // unquote quoted table name
203 : 42 : const sal_Unicode * pBuf = aTableNameBuffer.getStr();
204 [ - + ]: 42 : if( pBuf[ 0 ] == aQuote &&
[ - + # # ]
205 : 0 : pBuf[ aTableNameBuffer.getLength() - 1 ] == aQuote )
206 : : {
207 [ # # ]: 0 : ::rtl::OUString aName = aTableNameBuffer.makeStringAndClear();
208 : 0 : rOutTableName = aName.copy( 1, aName.getLength() - 2 );
209 : : }
210 : : else
211 [ + - ]: 42 : rOutTableName = aTableNameBuffer.makeStringAndClear();
212 : : }
213 : : else
214 : 24 : nDelimiterPos = nStartPos;
215 : :
216 [ + + ]: 132 : for( sal_Int32 i = 0;
217 : : nNextDelimiterPos < nEndPos;
218 : : nDelimiterPos = nNextDelimiterPos, i++ )
219 : : {
220 : 66 : nNextDelimiterPos = rXMLString.indexOf( aDot, nDelimiterPos + 1 );
221 [ + - ][ + + ]: 66 : if( nNextDelimiterPos == -1 ||
222 : : nNextDelimiterPos > nEndPos )
223 : 66 : nNextDelimiterPos = nEndPos + 1;
224 : :
225 [ + - ]: 66 : if( i==0 )
226 : : // only take first cell
227 : : lcl_getSingleCellAddressFromXMLString(
228 : 66 : rXMLString, nDelimiterPos + 1, nNextDelimiterPos - 1, rOutCell );
229 : : }
230 : :
231 : 66 : return true;
232 : : }
233 : :
234 : 42 : bool lcl_getCellRangeAddressFromXMLString(
235 : : const ::rtl::OUString& rXMLString,
236 : : sal_Int32 nStartPos, sal_Int32 nEndPos,
237 : : ::chart::XMLRangeHelper::CellRange & rOutRange )
238 : : {
239 : 42 : bool bResult = true;
240 : : static const sal_Unicode aColon( ':' );
241 : : static const sal_Unicode aQuote( '\'' );
242 : : static const sal_Unicode aBackslash( '\\' );
243 : :
244 : 42 : sal_Int32 nDelimiterPos = nStartPos;
245 : 42 : bool bInQuotation = false;
246 : : // parse table name
247 [ + + ]: 1206 : while( nDelimiterPos < nEndPos &&
[ + - + + ]
[ + + ]
248 : 594 : ( bInQuotation || rXMLString[ nDelimiterPos ] != aColon ))
249 : : {
250 : : // skip escaped characters (with backslash)
251 [ - + ]: 570 : if( rXMLString[ nDelimiterPos ] == aBackslash )
252 : 0 : ++nDelimiterPos;
253 : : // toggle quotation mode when finding single quotes
254 [ - + ]: 570 : else if( rXMLString[ nDelimiterPos ] == aQuote )
255 : 0 : bInQuotation = ! bInQuotation;
256 : :
257 : 570 : ++nDelimiterPos;
258 : : }
259 : :
260 [ + + ]: 42 : if( nDelimiterPos == nEndPos )
261 : : {
262 : : // only one cell
263 : : bResult = lcl_getCellAddressFromXMLString( rXMLString, nStartPos, nEndPos,
264 : : rOutRange.aUpperLeft,
265 : 18 : rOutRange.aTableName );
266 [ - + ]: 18 : if( rOutRange.aTableName.isEmpty() )
267 : 0 : bResult = false;
268 : : }
269 : : else
270 : : {
271 : : // range (separated by a colon)
272 : : bResult = lcl_getCellAddressFromXMLString( rXMLString, nStartPos, nDelimiterPos - 1,
273 : : rOutRange.aUpperLeft,
274 [ + - ]: 24 : rOutRange.aTableName );
275 [ - + ]: 24 : if( rOutRange.aTableName.isEmpty() )
276 : 0 : bResult = false;
277 : :
278 : 24 : ::rtl::OUString sTableSecondName;
279 [ + - ]: 24 : if( bResult )
280 : : {
281 : : bResult = lcl_getCellAddressFromXMLString( rXMLString, nDelimiterPos + 1, nEndPos,
282 : : rOutRange.aLowerRight,
283 [ + - ]: 24 : sTableSecondName );
284 : : }
285 [ + - - + : 48 : if( bResult &&
# # ][ - + ]
286 : 24 : !sTableSecondName.isEmpty() &&
287 : 0 : ! sTableSecondName.equals( rOutRange.aTableName ))
288 : 24 : bResult = false;
289 : : }
290 : :
291 : 42 : return bResult;
292 : : }
293 : :
294 : : } // anonymous namespace
295 : :
296 : : // ================================================================================
297 : :
298 : : namespace chart
299 : : {
300 : : namespace XMLRangeHelper
301 : : {
302 : :
303 : 42 : CellRange getCellRangeFromXMLString( const OUString & rXMLString )
304 : : {
305 : : static const sal_Unicode aSpace( ' ' );
306 : : static const sal_Unicode aQuote( '\'' );
307 : : // static const sal_Unicode aDoubleQuote( '\"' );
308 : : static const sal_Unicode aDollar( '$' );
309 : : static const sal_Unicode aBackslash( '\\' );
310 : :
311 : 42 : sal_Int32 nStartPos = 0;
312 : 42 : sal_Int32 nEndPos = nStartPos;
313 : 42 : const sal_Int32 nLength = rXMLString.getLength();
314 : :
315 : : // reset
316 [ + - ]: 42 : CellRange aResult;
317 : :
318 : : // iterate over different ranges
319 [ + + ]: 84 : for( sal_Int32 i = 0;
320 : : nEndPos < nLength;
321 : : nStartPos = ++nEndPos, i++ )
322 : : {
323 : : // find start point of next range
324 : :
325 : : // ignore leading '$'
326 [ - + ]: 42 : if( rXMLString[ nEndPos ] == aDollar)
327 : 0 : nEndPos++;
328 : :
329 : 42 : bool bInQuotation = false;
330 : : // parse range
331 [ + + ]: 1410 : while( nEndPos < nLength &&
[ + - + - ]
[ + + ]
332 : 684 : ( bInQuotation || rXMLString[ nEndPos ] != aSpace ))
333 : : {
334 : : // skip escaped characters (with backslash)
335 [ - + ]: 684 : if( rXMLString[ nEndPos ] == aBackslash )
336 : 0 : ++nEndPos;
337 : : // toggle quotation mode when finding single quotes
338 [ - + ]: 684 : else if( rXMLString[ nEndPos ] == aQuote )
339 : 0 : bInQuotation = ! bInQuotation;
340 : :
341 : 684 : ++nEndPos;
342 : : }
343 : :
344 [ - + ]: 42 : if( ! lcl_getCellRangeAddressFromXMLString(
345 : : rXMLString,
346 : : nStartPos, nEndPos - 1,
347 [ + - ]: 42 : aResult ))
348 : : {
349 : : // if an error occurred, bail out
350 [ # # ]: 0 : return CellRange();
351 : : }
352 : : }
353 : :
354 [ + - ][ + - ]: 42 : return aResult;
355 : : }
356 : :
357 : 136 : OUString getXMLStringFromCellRange( const CellRange & rRange )
358 : : {
359 : : static const sal_Unicode aSpace( ' ' );
360 : : static const sal_Unicode aQuote( '\'' );
361 : :
362 : 136 : ::rtl::OUStringBuffer aBuffer;
363 : :
364 [ + - ]: 136 : if( !(rRange.aTableName).isEmpty())
365 : : {
366 : 136 : bool bNeedsEscaping = ( rRange.aTableName.indexOf( aQuote ) > -1 );
367 [ - + ][ + - ]: 136 : bool bNeedsQuoting = bNeedsEscaping || ( rRange.aTableName.indexOf( aSpace ) > -1 );
368 : :
369 : : // quote table name if it contains spaces or quotes
370 [ - + ]: 136 : if( bNeedsQuoting )
371 : : {
372 : : // leading quote
373 [ # # ]: 0 : aBuffer.append( aQuote );
374 : :
375 : : // escape existing quotes
376 [ # # ]: 0 : if( bNeedsEscaping )
377 : : {
378 : 0 : const sal_Unicode * pTableNameBeg = rRange.aTableName.getStr();
379 : :
380 : : // append the quoted string at the buffer
381 : : ::std::for_each( pTableNameBeg,
382 : 0 : pTableNameBeg + rRange.aTableName.getLength(),
383 [ # # ]: 0 : lcl_Escape( aBuffer ) );
384 : : }
385 : : else
386 [ # # ]: 0 : aBuffer.append( rRange.aTableName );
387 : :
388 : : // final quote
389 [ # # ]: 0 : aBuffer.append( aQuote );
390 : : }
391 : : else
392 [ + - ]: 136 : aBuffer.append( rRange.aTableName );
393 : : }
394 [ + - ]: 136 : lcl_getXMLStringForCell( rRange.aUpperLeft, &aBuffer );
395 : :
396 [ + + ]: 136 : if( ! rRange.aLowerRight.empty())
397 : : {
398 : : // we have a range (not a single cell)
399 [ + - ]: 88 : aBuffer.append( sal_Unicode( ':' ));
400 [ + - ]: 88 : lcl_getXMLStringForCell( rRange.aLowerRight, &aBuffer );
401 : : }
402 : :
403 [ + - ]: 136 : return aBuffer.makeStringAndClear();
404 : : }
405 : :
406 : : } // namespace XMLRangeHelper
407 : : } // namespace chart
408 : :
409 : : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|