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_sw.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 nDelimiterPos >= nEndPos ) 200 { 201 return false; 202 } 203 if( nDelimiterPos > nStartPos ) 204 { 205 // there is a table name before the address 206 207 ::rtl::OUStringBuffer aTableNameBuffer; 208 const sal_Unicode * pTableName = rXMLString.getStr(); 209 210 // remove escapes from table name 211 ::std::for_each( pTableName + nStartPos, 212 pTableName + nDelimiterPos, 213 lcl_UnEscape( aTableNameBuffer )); 214 215 // unquote quoted table name 216 const sal_Unicode * pBuf = aTableNameBuffer.getStr(); 217 if( pBuf[ 0 ] == aQuote && 218 pBuf[ aTableNameBuffer.getLength() - 1 ] == aQuote ) 219 { 220 ::rtl::OUString aName = aTableNameBuffer.makeStringAndClear(); 221 rOutTableName = aName.copy( 1, aName.getLength() - 2 ); 222 } 223 else 224 rOutTableName = aTableNameBuffer.makeStringAndClear(); 225 } 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 } 278 else 279 { 280 // range (separated by a colon) 281 bResult = lcl_getCellAddressFromXMLString( rXMLString, nStartPos, nDelimiterPos - 1, 282 rOutRange.aUpperLeft, 283 rOutRange.aTableName ); 284 ::rtl::OUString sTableSecondName; 285 if( bResult ) 286 { 287 bResult = lcl_getCellAddressFromXMLString( rXMLString, nDelimiterPos + 1, nEndPos, 288 rOutRange.aLowerRight, 289 sTableSecondName ); 290 } 291 if( bResult && 292 sTableSecondName.getLength() && 293 ! sTableSecondName.equals( rOutRange.aTableName )) 294 bResult = false; 295 } 296 297 return bResult; 298 } 299 300 } // anonymous namespace 301 302 // ================================================================================ 303 304 //namespace chart 305 //{ 306 namespace XMLRangeHelper 307 { 308 309 CellRange getCellRangeFromXMLString( const OUString & rXMLString ) 310 { 311 static const sal_Unicode aSpace( ' ' ); 312 static const sal_Unicode aQuote( '\'' ); 313 static const sal_Unicode aDollar( '$' ); 314 static const sal_Unicode aBackslash( '\\' ); 315 316 sal_Int32 nStartPos = 0; 317 sal_Int32 nEndPos = nStartPos; 318 const sal_Int32 nLength = rXMLString.getLength(); 319 320 // reset 321 CellRange aResult; 322 323 // iterate over different ranges 324 for( sal_Int32 i = 0; 325 nEndPos < nLength; 326 nStartPos = ++nEndPos, i++ ) 327 { 328 // find start point of next range 329 330 // ignore leading '$' 331 if( rXMLString[ nEndPos ] == aDollar) 332 nEndPos++; 333 334 bool bInQuotation = false; 335 // parse range 336 while( nEndPos < nLength && 337 ( bInQuotation || rXMLString[ nEndPos ] != aSpace )) 338 { 339 // skip escaped characters (with backslash) 340 if( rXMLString[ nEndPos ] == aBackslash ) 341 ++nEndPos; 342 // toggle quotation mode when finding single quotes 343 else if( rXMLString[ nEndPos ] == aQuote ) 344 bInQuotation = ! bInQuotation; 345 346 ++nEndPos; 347 } 348 349 if( ! lcl_getCellRangeAddressFromXMLString( 350 rXMLString, 351 nStartPos, nEndPos - 1, 352 aResult )) 353 { 354 // if an error occured, bail out 355 return CellRange(); 356 } 357 } 358 359 return aResult; 360 } 361 362 OUString getXMLStringFromCellRange( const CellRange & rRange ) 363 { 364 static const sal_Unicode aSpace( ' ' ); 365 static const sal_Unicode aQuote( '\'' ); 366 367 ::rtl::OUStringBuffer aBuffer; 368 369 if( (rRange.aTableName).getLength()) 370 { 371 bool bNeedsEscaping = ( rRange.aTableName.indexOf( aQuote ) > -1 ); 372 bool bNeedsQuoting = bNeedsEscaping || ( rRange.aTableName.indexOf( aSpace ) > -1 ); 373 374 // quote table name if it contains spaces or quotes 375 if( bNeedsQuoting ) 376 { 377 // leading quote 378 aBuffer.append( aQuote ); 379 380 // escape existing quotes 381 if( bNeedsEscaping ) 382 { 383 const sal_Unicode * pTableNameBeg = rRange.aTableName.getStr(); 384 385 // append the quoted string at the buffer 386 ::std::for_each( pTableNameBeg, 387 pTableNameBeg + rRange.aTableName.getLength(), 388 lcl_Escape( aBuffer ) ); 389 } 390 else 391 aBuffer.append( rRange.aTableName ); 392 393 // final quote 394 aBuffer.append( aQuote ); 395 } 396 else 397 aBuffer.append( rRange.aTableName ); 398 } 399 aBuffer.append( lcl_getXMLStringForCell( rRange.aUpperLeft )); 400 401 if( ! rRange.aLowerRight.empty()) 402 { 403 // we have a range (not a single cell) 404 aBuffer.append( sal_Unicode( ':' )); 405 aBuffer.append( lcl_getXMLStringForCell( rRange.aLowerRight )); 406 } 407 408 return aBuffer.makeStringAndClear(); 409 } 410 411 } // namespace XMLRangeHelper 412 //} // namespace chart 413