| /************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *************************************************************/ |
| |
| |
| |
| // MARKER(update_precomp.py): autogen include statement, do not remove |
| #include "precompiled_chart2.hxx" |
| |
| #include "XMLRangeHelper.hxx" |
| #include <unotools/charclass.hxx> |
| #include <rtl/ustrbuf.hxx> |
| |
| #include <algorithm> |
| #include <functional> |
| |
| using ::rtl::OUString; |
| using ::rtl::OUStringBuffer; |
| |
| // ================================================================================ |
| |
| namespace |
| { |
| /** unary function that escapes backslashes and single quotes in a sal_Unicode |
| array (which you can get from an OUString with getStr()) and puts the result |
| into the OUStringBuffer given in the CTOR |
| */ |
| class lcl_Escape : public ::std::unary_function< sal_Unicode, void > |
| { |
| public: |
| lcl_Escape( ::rtl::OUStringBuffer & aResultBuffer ) : m_aResultBuffer( aResultBuffer ) {} |
| void operator() ( sal_Unicode aChar ) |
| { |
| static const sal_Unicode m_aQuote( '\'' ); |
| static const sal_Unicode m_aBackslash( '\\' ); |
| |
| if( aChar == m_aQuote || |
| aChar == m_aBackslash ) |
| m_aResultBuffer.append( m_aBackslash ); |
| m_aResultBuffer.append( aChar ); |
| } |
| |
| private: |
| ::rtl::OUStringBuffer & m_aResultBuffer; |
| }; |
| |
| // ---------------------------------------- |
| |
| /** unary function that removes backslash escapes in a sal_Unicode array (which |
| you can get from an OUString with getStr()) and puts the result into the |
| OUStringBuffer given in the CTOR |
| */ |
| class lcl_UnEscape : public ::std::unary_function< sal_Unicode, void > |
| { |
| public: |
| lcl_UnEscape( ::rtl::OUStringBuffer & aResultBuffer ) : m_aResultBuffer( aResultBuffer ) {} |
| void operator() ( sal_Unicode aChar ) |
| { |
| static const sal_Unicode m_aBackslash( '\\' ); |
| |
| if( aChar != m_aBackslash ) |
| m_aResultBuffer.append( aChar ); |
| } |
| |
| private: |
| ::rtl::OUStringBuffer & m_aResultBuffer; |
| }; |
| |
| // ---------------------------------------- |
| |
| OUStringBuffer lcl_getXMLStringForCell( const ::chart::XMLRangeHelper::Cell & rCell ) |
| { |
| ::rtl::OUStringBuffer aBuffer; |
| if( rCell.empty()) |
| return aBuffer; |
| |
| sal_Int32 nCol = rCell.nColumn; |
| aBuffer.append( (sal_Unicode)'.' ); |
| if( ! rCell.bRelativeColumn ) |
| aBuffer.append( (sal_Unicode)'$' ); |
| |
| // get A, B, C, ..., AA, AB, ... representation of column number |
| if( nCol < 26 ) |
| aBuffer.append( (sal_Unicode)('A' + nCol) ); |
| else if( nCol < 702 ) |
| { |
| aBuffer.append( (sal_Unicode)('A' + nCol / 26 - 1 )); |
| aBuffer.append( (sal_Unicode)('A' + nCol % 26) ); |
| } |
| else // works for nCol <= 18,278 |
| { |
| aBuffer.append( (sal_Unicode)('A' + nCol / 702 - 1 )); |
| aBuffer.append( (sal_Unicode)('A' + (nCol % 702) / 26 )); |
| aBuffer.append( (sal_Unicode)('A' + nCol % 26) ); |
| } |
| |
| // write row number as number |
| if( ! rCell.bRelativeRow ) |
| aBuffer.append( (sal_Unicode)'$' ); |
| aBuffer.append( rCell.nRow + (sal_Int32)1 ); |
| |
| return aBuffer; |
| } |
| |
| void lcl_getSingleCellAddressFromXMLString( |
| const ::rtl::OUString& rXMLString, |
| sal_Int32 nStartPos, sal_Int32 nEndPos, |
| ::chart::XMLRangeHelper::Cell & rOutCell ) |
| { |
| // expect "\$?[a-zA-Z]+\$?[1-9][0-9]*" |
| static const sal_Unicode aDollar( '$' ); |
| static const sal_Unicode aLetterA( 'A' ); |
| |
| ::rtl::OUString aCellStr = rXMLString.copy( nStartPos, nEndPos - nStartPos + 1 ).toAsciiUpperCase(); |
| const sal_Unicode* pStrArray = aCellStr.getStr(); |
| sal_Int32 nLength = aCellStr.getLength(); |
| sal_Int32 i = nLength - 1, nColumn = 0; |
| |
| // parse number for row |
| while( CharClass::isAsciiDigit( pStrArray[ i ] ) && i >= 0 ) |
| i--; |
| rOutCell.nRow = (aCellStr.copy( i + 1 )).toInt32() - 1; |
| // a dollar in XML means absolute (whereas in UI it means relative) |
| if( pStrArray[ i ] == aDollar ) |
| { |
| i--; |
| rOutCell.bRelativeRow = false; |
| } |
| else |
| rOutCell.bRelativeRow = true; |
| |
| // parse rest for column |
| sal_Int32 nPower = 1; |
| while( CharClass::isAsciiAlpha( pStrArray[ i ] )) |
| { |
| nColumn += (pStrArray[ i ] - aLetterA + 1) * nPower; |
| i--; |
| nPower *= 26; |
| } |
| rOutCell.nColumn = nColumn - 1; |
| |
| rOutCell.bRelativeColumn = true; |
| if( i >= 0 && |
| pStrArray[ i ] == aDollar ) |
| rOutCell.bRelativeColumn = false; |
| rOutCell.bIsEmpty = false; |
| } |
| |
| bool lcl_getCellAddressFromXMLString( |
| const ::rtl::OUString& rXMLString, |
| sal_Int32 nStartPos, sal_Int32 nEndPos, |
| ::chart::XMLRangeHelper::Cell & rOutCell, |
| ::rtl::OUString& rOutTableName ) |
| { |
| static const sal_Unicode aDot( '.' ); |
| static const sal_Unicode aQuote( '\'' ); |
| static const sal_Unicode aBackslash( '\\' ); |
| |
| sal_Int32 nNextDelimiterPos = nStartPos; |
| |
| sal_Int32 nDelimiterPos = nStartPos; |
| bool bInQuotation = false; |
| // parse table name |
| while( nDelimiterPos < nEndPos && |
| ( bInQuotation || rXMLString[ nDelimiterPos ] != aDot )) |
| { |
| // skip escaped characters (with backslash) |
| if( rXMLString[ nDelimiterPos ] == aBackslash ) |
| ++nDelimiterPos; |
| // toggle quotation mode when finding single quotes |
| else if( rXMLString[ nDelimiterPos ] == aQuote ) |
| bInQuotation = ! bInQuotation; |
| |
| ++nDelimiterPos; |
| } |
| |
| if( nDelimiterPos == -1 ) |
| return false; |
| |
| if( nDelimiterPos > nStartPos && nDelimiterPos < nEndPos ) |
| { |
| // there is a table name before the address |
| |
| ::rtl::OUStringBuffer aTableNameBuffer; |
| const sal_Unicode * pTableName = rXMLString.getStr(); |
| |
| // remove escapes from table name |
| ::std::for_each( pTableName + nStartPos, |
| pTableName + nDelimiterPos, |
| lcl_UnEscape( aTableNameBuffer )); |
| |
| // unquote quoted table name |
| const sal_Unicode * pBuf = aTableNameBuffer.getStr(); |
| if( pBuf[ 0 ] == aQuote && |
| pBuf[ aTableNameBuffer.getLength() - 1 ] == aQuote ) |
| { |
| ::rtl::OUString aName = aTableNameBuffer.makeStringAndClear(); |
| rOutTableName = aName.copy( 1, aName.getLength() - 2 ); |
| } |
| else |
| rOutTableName = aTableNameBuffer.makeStringAndClear(); |
| } |
| else |
| nDelimiterPos = nStartPos; |
| |
| for( sal_Int32 i = 0; |
| nNextDelimiterPos < nEndPos; |
| nDelimiterPos = nNextDelimiterPos, i++ ) |
| { |
| nNextDelimiterPos = rXMLString.indexOf( aDot, nDelimiterPos + 1 ); |
| if( nNextDelimiterPos == -1 || |
| nNextDelimiterPos > nEndPos ) |
| nNextDelimiterPos = nEndPos + 1; |
| |
| if( i==0 ) |
| // only take first cell |
| lcl_getSingleCellAddressFromXMLString( |
| rXMLString, nDelimiterPos + 1, nNextDelimiterPos - 1, rOutCell ); |
| } |
| |
| return true; |
| } |
| |
| bool lcl_getCellRangeAddressFromXMLString( |
| const ::rtl::OUString& rXMLString, |
| sal_Int32 nStartPos, sal_Int32 nEndPos, |
| ::chart::XMLRangeHelper::CellRange & rOutRange ) |
| { |
| bool bResult = true; |
| static const sal_Unicode aColon( ':' ); |
| static const sal_Unicode aQuote( '\'' ); |
| static const sal_Unicode aBackslash( '\\' ); |
| |
| sal_Int32 nDelimiterPos = nStartPos; |
| bool bInQuotation = false; |
| // parse table name |
| while( nDelimiterPos < nEndPos && |
| ( bInQuotation || rXMLString[ nDelimiterPos ] != aColon )) |
| { |
| // skip escaped characters (with backslash) |
| if( rXMLString[ nDelimiterPos ] == aBackslash ) |
| ++nDelimiterPos; |
| // toggle quotation mode when finding single quotes |
| else if( rXMLString[ nDelimiterPos ] == aQuote ) |
| bInQuotation = ! bInQuotation; |
| |
| ++nDelimiterPos; |
| } |
| |
| if( nDelimiterPos == nEndPos ) |
| { |
| // only one cell |
| bResult = lcl_getCellAddressFromXMLString( rXMLString, nStartPos, nEndPos, |
| rOutRange.aUpperLeft, |
| rOutRange.aTableName ); |
| if( rOutRange.aTableName.isEmpty() ) |
| bResult = false; |
| } |
| else |
| { |
| // range (separated by a colon) |
| bResult = lcl_getCellAddressFromXMLString( rXMLString, nStartPos, nDelimiterPos - 1, |
| rOutRange.aUpperLeft, |
| rOutRange.aTableName ); |
| if( rOutRange.aTableName.isEmpty() ) |
| bResult = false; |
| |
| ::rtl::OUString sTableSecondName; |
| if( bResult ) |
| { |
| bResult = lcl_getCellAddressFromXMLString( rXMLString, nDelimiterPos + 1, nEndPos, |
| rOutRange.aLowerRight, |
| sTableSecondName ); |
| } |
| if( bResult && |
| !sTableSecondName.isEmpty() && |
| ! sTableSecondName.equals( rOutRange.aTableName )) |
| bResult = false; |
| } |
| |
| return bResult; |
| } |
| |
| } // anonymous namespace |
| |
| // ================================================================================ |
| |
| namespace chart |
| { |
| namespace XMLRangeHelper |
| { |
| |
| CellRange getCellRangeFromXMLString( const OUString & rXMLString ) |
| { |
| static const sal_Unicode aSpace( ' ' ); |
| static const sal_Unicode aQuote( '\'' ); |
| // static const sal_Unicode aDoubleQuote( '\"' ); |
| static const sal_Unicode aDollar( '$' ); |
| static const sal_Unicode aBackslash( '\\' ); |
| |
| sal_Int32 nStartPos = 0; |
| sal_Int32 nEndPos = nStartPos; |
| const sal_Int32 nLength = rXMLString.getLength(); |
| |
| // reset |
| CellRange aResult; |
| |
| // iterate over different ranges |
| for( sal_Int32 i = 0; |
| nEndPos < nLength; |
| nStartPos = ++nEndPos, i++ ) |
| { |
| // find start point of next range |
| |
| // ignore leading '$' |
| if( rXMLString[ nEndPos ] == aDollar) |
| nEndPos++; |
| |
| bool bInQuotation = false; |
| // parse range |
| while( nEndPos < nLength && |
| ( bInQuotation || rXMLString[ nEndPos ] != aSpace )) |
| { |
| // skip escaped characters (with backslash) |
| if( rXMLString[ nEndPos ] == aBackslash ) |
| ++nEndPos; |
| // toggle quotation mode when finding single quotes |
| else if( rXMLString[ nEndPos ] == aQuote ) |
| bInQuotation = ! bInQuotation; |
| |
| ++nEndPos; |
| } |
| |
| if( ! lcl_getCellRangeAddressFromXMLString( |
| rXMLString, |
| nStartPos, nEndPos - 1, |
| aResult )) |
| { |
| // if an error occurred, bail out |
| return CellRange(); |
| } |
| } |
| |
| return aResult; |
| } |
| |
| OUString getXMLStringFromCellRange( const CellRange & rRange ) |
| { |
| static const sal_Unicode aSpace( ' ' ); |
| static const sal_Unicode aQuote( '\'' ); |
| |
| ::rtl::OUStringBuffer aBuffer; |
| |
| if( !rRange.aTableName.isEmpty()) |
| { |
| bool bNeedsEscaping = ( rRange.aTableName.indexOf( aQuote ) > -1 ); |
| bool bNeedsQuoting = bNeedsEscaping || ( rRange.aTableName.indexOf( aSpace ) > -1 ); |
| |
| // quote table name if it contains spaces or quotes |
| if( bNeedsQuoting ) |
| { |
| // leading quote |
| aBuffer.append( aQuote ); |
| |
| // escape existing quotes |
| if( bNeedsEscaping ) |
| { |
| const sal_Unicode * pTableNameBeg = rRange.aTableName.getStr(); |
| |
| // append the quoted string at the buffer |
| ::std::for_each( pTableNameBeg, |
| pTableNameBeg + rRange.aTableName.getLength(), |
| lcl_Escape( aBuffer ) ); |
| } |
| else |
| aBuffer.append( rRange.aTableName ); |
| |
| // final quote |
| aBuffer.append( aQuote ); |
| } |
| else |
| aBuffer.append( rRange.aTableName ); |
| } |
| aBuffer.append( lcl_getXMLStringForCell( rRange.aUpperLeft )); |
| |
| if( ! rRange.aLowerRight.empty()) |
| { |
| // we have a range (not a single cell) |
| aBuffer.append( sal_Unicode( ':' )); |
| aBuffer.append( lcl_getXMLStringForCell( rRange.aLowerRight )); |
| } |
| |
| return aBuffer.makeStringAndClear(); |
| } |
| |
| } // namespace XMLRangeHelper |
| } // namespace chart |