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