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