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