blob: bf04895dcbcb36c5b586f6029d1c76a599293ef3 [file] [log] [blame]
/**************************************************************
*
* 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.
*
*************************************************************/
#include "oox/xls/condformatbuffer.hxx"
#include <com/sun/star/beans/PropertyValue.hpp>
#include <com/sun/star/container/XIndexAccess.hpp>
#include <com/sun/star/container/XNameContainer.hpp>
#include <com/sun/star/sheet/ConditionOperator.hpp>
#include <com/sun/star/sheet/XSheetCellRanges.hpp>
#include <com/sun/star/sheet/XSheetConditionalEntries.hpp>
#include <com/sun/star/sheet/XSpreadsheet.hpp>
#include <com/sun/star/sheet/XSpreadsheetDocument.hpp>
#include <com/sun/star/sheet/XSpreadsheets.hpp>
#include <com/sun/star/style/XStyle.hpp>
#include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
#include <com/sun/star/table/CellAddress.hpp>
#include <com/sun/star/table/CellRangeAddress.hpp>
#include <com/sun/star/table/XCellRange.hpp>
#include <rtl/ustrbuf.hxx>
#include "oox/helper/attributelist.hxx"
#include "oox/helper/containerhelper.hxx"
#include "oox/helper/propertyset.hxx"
#include "oox/xls/addressconverter.hxx"
#include "oox/xls/biffinputstream.hxx"
#include "oox/xls/stylesbuffer.hxx"
namespace oox {
namespace xls {
// ============================================================================
using namespace ::com::sun::star::beans;
using namespace ::com::sun::star::container;
using namespace ::com::sun::star::sheet;
using namespace ::com::sun::star::style;
using namespace ::com::sun::star::table;
using namespace ::com::sun::star::uno;
using ::rtl::OUString;
using ::rtl::OUStringBuffer;
// ============================================================================
namespace {
const sal_Int32 BIFF12_CFRULE_TYPE_CELLIS = 1;
const sal_Int32 BIFF12_CFRULE_TYPE_EXPRESSION = 2;
const sal_Int32 BIFF12_CFRULE_TYPE_COLORSCALE = 3;
const sal_Int32 BIFF12_CFRULE_TYPE_DATABAR = 4;
const sal_Int32 BIFF12_CFRULE_TYPE_TOPTEN = 5;
const sal_Int32 BIFF12_CFRULE_TYPE_ICONSET = 6;
const sal_Int32 BIFF12_CFRULE_SUB_CELLIS = 0;
const sal_Int32 BIFF12_CFRULE_SUB_EXPRESSION = 1;
const sal_Int32 BIFF12_CFRULE_SUB_COLORSCALE = 2;
const sal_Int32 BIFF12_CFRULE_SUB_DATABAR = 3;
const sal_Int32 BIFF12_CFRULE_SUB_ICONSET = 4;
const sal_Int32 BIFF12_CFRULE_SUB_TOPTEN = 5;
const sal_Int32 BIFF12_CFRULE_SUB_UNIQUE = 7;
const sal_Int32 BIFF12_CFRULE_SUB_TEXT = 8;
const sal_Int32 BIFF12_CFRULE_SUB_BLANK = 9;
const sal_Int32 BIFF12_CFRULE_SUB_NOTBLANK = 10;
const sal_Int32 BIFF12_CFRULE_SUB_ERROR = 11;
const sal_Int32 BIFF12_CFRULE_SUB_NOTERROR = 12;
const sal_Int32 BIFF12_CFRULE_SUB_TODAY = 15;
const sal_Int32 BIFF12_CFRULE_SUB_TOMORROW = 16;
const sal_Int32 BIFF12_CFRULE_SUB_YESTERDAY = 17;
const sal_Int32 BIFF12_CFRULE_SUB_LAST7DAYS = 18;
const sal_Int32 BIFF12_CFRULE_SUB_LASTMONTH = 19;
const sal_Int32 BIFF12_CFRULE_SUB_NEXTMONTH = 20;
const sal_Int32 BIFF12_CFRULE_SUB_THISWEEK = 21;
const sal_Int32 BIFF12_CFRULE_SUB_NEXTWEEK = 22;
const sal_Int32 BIFF12_CFRULE_SUB_LASTWEEK = 23;
const sal_Int32 BIFF12_CFRULE_SUB_THISMONTH = 24;
const sal_Int32 BIFF12_CFRULE_SUB_ABOVEAVERAGE = 25;
const sal_Int32 BIFF12_CFRULE_SUB_BELOWAVERAGE = 26;
const sal_Int32 BIFF12_CFRULE_SUB_DUPLICATE = 27;
const sal_Int32 BIFF12_CFRULE_SUB_EQABOVEAVERAGE = 29;
const sal_Int32 BIFF12_CFRULE_SUB_EQBELOWAVERAGE = 30;
const sal_Int32 BIFF12_CFRULE_TIMEOP_TODAY = 0;
const sal_Int32 BIFF12_CFRULE_TIMEOP_YESTERDAY = 1;
const sal_Int32 BIFF12_CFRULE_TIMEOP_LAST7DAYS = 2;
const sal_Int32 BIFF12_CFRULE_TIMEOP_THISWEEK = 3;
const sal_Int32 BIFF12_CFRULE_TIMEOP_LASTWEEK = 4;
const sal_Int32 BIFF12_CFRULE_TIMEOP_LASTMONTH = 5;
const sal_Int32 BIFF12_CFRULE_TIMEOP_TOMORROW = 6;
const sal_Int32 BIFF12_CFRULE_TIMEOP_NEXTWEEK = 7;
const sal_Int32 BIFF12_CFRULE_TIMEOP_NEXTMONTH = 8;
const sal_Int32 BIFF12_CFRULE_TIMEOP_THISMONTH = 9;
const sal_uInt16 BIFF12_CFRULE_STOPIFTRUE = 0x0002;
const sal_uInt16 BIFF12_CFRULE_ABOVEAVERAGE = 0x0004;
const sal_uInt16 BIFF12_CFRULE_BOTTOM = 0x0008;
const sal_uInt16 BIFF12_CFRULE_PERCENT = 0x0010;
// ----------------------------------------------------------------------------
template< typename Type >
void lclAppendProperty( ::std::vector< PropertyValue >& orProps, const OUString& rPropName, const Type& rValue )
{
orProps.push_back( PropertyValue() );
orProps.back().Name = rPropName;
orProps.back().Value <<= rValue;
}
} // namespace
// ============================================================================
CondFormatRuleModel::CondFormatRuleModel() :
mnPriority( -1 ),
mnType( XML_TOKEN_INVALID ),
mnOperator( XML_TOKEN_INVALID ),
mnTimePeriod( XML_TOKEN_INVALID ),
mnRank( 0 ),
mnStdDev( 0 ),
mnDxfId( -1 ),
mbStopIfTrue( false ),
mbBottom( false ),
mbPercent( false ),
mbAboveAverage( true ),
mbEqualAverage( false )
{
}
void CondFormatRuleModel::setBiffOperator( sal_Int32 nOperator )
{
static const sal_Int32 spnOperators[] = {
XML_TOKEN_INVALID, XML_between, XML_notBetween, XML_equal, XML_notEqual,
XML_greaterThan, XML_lessThan, XML_greaterThanOrEqual, XML_lessThanOrEqual };
mnOperator = STATIC_ARRAY_SELECT( spnOperators, nOperator, XML_TOKEN_INVALID );
}
void CondFormatRuleModel::setBiff12TextType( sal_Int32 nOperator )
{
// note: type XML_notContainsText vs. operator XML_notContains
static const sal_Int32 spnTypes[] = { XML_containsText, XML_notContainsText, XML_beginsWith, XML_endsWith };
mnType = STATIC_ARRAY_SELECT( spnTypes, nOperator, XML_TOKEN_INVALID );
static const sal_Int32 spnOperators[] = { XML_containsText, XML_notContains, XML_beginsWith, XML_endsWith };
mnOperator = STATIC_ARRAY_SELECT( spnOperators, nOperator, XML_TOKEN_INVALID );
}
// ============================================================================
CondFormatRule::CondFormatRule( const CondFormat& rCondFormat ) :
WorksheetHelper( rCondFormat ),
mrCondFormat( rCondFormat )
{
}
void CondFormatRule::importCfRule( const AttributeList& rAttribs )
{
maModel.maText = rAttribs.getString( XML_text, OUString() );
maModel.mnPriority = rAttribs.getInteger( XML_priority, -1 );
maModel.mnType = rAttribs.getToken( XML_type, XML_TOKEN_INVALID );
maModel.mnOperator = rAttribs.getToken( XML_operator, XML_TOKEN_INVALID );
maModel.mnTimePeriod = rAttribs.getToken( XML_timePeriod, XML_TOKEN_INVALID );
maModel.mnRank = rAttribs.getInteger( XML_rank, 0 );
maModel.mnStdDev = rAttribs.getInteger( XML_stdDev, 0 );
maModel.mnDxfId = rAttribs.getInteger( XML_dxfId, -1 );
maModel.mbStopIfTrue = rAttribs.getBool( XML_stopIfTrue, false );
maModel.mbBottom = rAttribs.getBool( XML_bottom, false );
maModel.mbPercent = rAttribs.getBool( XML_percent, false );
maModel.mbAboveAverage = rAttribs.getBool( XML_aboveAverage, true );
maModel.mbEqualAverage = rAttribs.getBool( XML_equalAverage, false );
}
void CondFormatRule::appendFormula( const OUString& rFormula )
{
CellAddress aBaseAddr = mrCondFormat.getRanges().getBaseAddress();
ApiTokenSequence aTokens = getFormulaParser().importFormula( aBaseAddr, rFormula );
maModel.maFormulas.push_back( aTokens );
}
void CondFormatRule::importCfRule( SequenceInputStream& rStrm )
{
sal_Int32 nType, nSubType, nOperator, nFmla1Size, nFmla2Size, nFmla3Size;
sal_uInt16 nFlags;
rStrm >> nType >> nSubType >> maModel.mnDxfId >> maModel.mnPriority >> nOperator;
rStrm.skip( 8 );
rStrm >> nFlags >> nFmla1Size >> nFmla2Size >> nFmla3Size >> maModel.maText;
/* Import the formulas. For no obvious reason, the sizes of the formulas
are already stored before. Nevertheless the following formulas contain
their own sizes. */
// first formula
OSL_ENSURE( (nFmla1Size >= 0) || ((nFmla2Size == 0) && (nFmla3Size == 0)), "CondFormatRule::importCfRule - missing first formula" );
OSL_ENSURE( (nFmla1Size > 0) == (rStrm.getRemaining() >= 8), "CondFormatRule::importCfRule - formula size mismatch" );
if( rStrm.getRemaining() >= 8 )
{
CellAddress aBaseAddr = mrCondFormat.getRanges().getBaseAddress();
ApiTokenSequence aTokens = getFormulaParser().importFormula( aBaseAddr, FORMULATYPE_CONDFORMAT, rStrm );
maModel.maFormulas.push_back( aTokens );
// second formula
OSL_ENSURE( (nFmla2Size >= 0) || (nFmla3Size == 0), "CondFormatRule::importCfRule - missing second formula" );
OSL_ENSURE( (nFmla2Size > 0) == (rStrm.getRemaining() >= 8), "CondFormatRule::importCfRule - formula size mismatch" );
if( rStrm.getRemaining() >= 8 )
{
aTokens = getFormulaParser().importFormula( aBaseAddr, FORMULATYPE_CONDFORMAT, rStrm );
maModel.maFormulas.push_back( aTokens );
// third formula
OSL_ENSURE( (nFmla3Size > 0) == (rStrm.getRemaining() >= 8), "CondFormatRule::importCfRule - formula size mismatch" );
if( rStrm.getRemaining() >= 8 )
{
aTokens = getFormulaParser().importFormula( aBaseAddr, FORMULATYPE_CONDFORMAT, rStrm );
maModel.maFormulas.push_back( aTokens );
}
}
}
// flags
maModel.mbStopIfTrue = getFlag( nFlags, BIFF12_CFRULE_STOPIFTRUE );
maModel.mbBottom = getFlag( nFlags, BIFF12_CFRULE_BOTTOM );
maModel.mbPercent = getFlag( nFlags, BIFF12_CFRULE_PERCENT );
maModel.mbAboveAverage = getFlag( nFlags, BIFF12_CFRULE_ABOVEAVERAGE );
// no flag for equalAverage, must be determined from subtype below...
// Convert the type/operator settings. This is a real mess...
switch( nType )
{
case BIFF12_CFRULE_TYPE_CELLIS:
OSL_ENSURE( nSubType == BIFF12_CFRULE_SUB_CELLIS, "CondFormatRule::importCfRule - rule type/subtype mismatch" );
maModel.mnType = XML_cellIs;
maModel.setBiffOperator( nOperator );
OSL_ENSURE( maModel.mnOperator != XML_TOKEN_INVALID, "CondFormatRule::importCfRule - unknown operator" );
break;
case BIFF12_CFRULE_TYPE_EXPRESSION:
// here we have to look at the subtype to find the real type...
switch( nSubType )
{
case BIFF12_CFRULE_SUB_EXPRESSION:
OSL_ENSURE( nOperator == 0, "CondFormatRule::importCfRule - unexpected operator value" );
maModel.mnType = XML_expression;
break;
case BIFF12_CFRULE_SUB_UNIQUE:
OSL_ENSURE( nOperator == 0, "CondFormatRule::importCfRule - unexpected operator value" );
maModel.mnType = XML_uniqueValues;
break;
case BIFF12_CFRULE_SUB_TEXT:
maModel.setBiff12TextType( nOperator );
OSL_ENSURE( maModel.mnType != XML_TOKEN_INVALID, "CondFormatRule::importCfRule - unexpected operator value" );
break;
case BIFF12_CFRULE_SUB_BLANK:
OSL_ENSURE( nOperator == 0, "CondFormatRule::importCfRule - unexpected operator value" );
maModel.mnType = XML_containsBlanks;
break;
case BIFF12_CFRULE_SUB_NOTBLANK:
OSL_ENSURE( nOperator == 0, "CondFormatRule::importCfRule - unexpected operator value" );
maModel.mnType = XML_notContainsBlanks;
break;
case BIFF12_CFRULE_SUB_ERROR:
OSL_ENSURE( nOperator == 0, "CondFormatRule::importCfRule - unexpected operator value" );
maModel.mnType = XML_containsErrors;
break;
case BIFF12_CFRULE_SUB_NOTERROR:
OSL_ENSURE( nOperator == 0, "CondFormatRule::importCfRule - unexpected operator value" );
maModel.mnType = XML_notContainsErrors;
break;
case BIFF12_CFRULE_SUB_TODAY:
OSL_ENSURE( nOperator == BIFF12_CFRULE_TIMEOP_TODAY, "CondFormatRule::importCfRule - unexpected time operator value" );
maModel.mnType = XML_timePeriod;
maModel.mnTimePeriod = XML_today;
break;
case BIFF12_CFRULE_SUB_TOMORROW:
OSL_ENSURE( nOperator == BIFF12_CFRULE_TIMEOP_TOMORROW, "CondFormatRule::importCfRule - unexpected time operator value" );
maModel.mnType = XML_timePeriod;
maModel.mnTimePeriod = XML_tomorrow;
break;
case BIFF12_CFRULE_SUB_YESTERDAY:
OSL_ENSURE( nOperator == BIFF12_CFRULE_TIMEOP_YESTERDAY, "CondFormatRule::importCfRule - unexpected time operator value" );
maModel.mnType = XML_timePeriod;
maModel.mnTimePeriod = XML_yesterday;
break;
case BIFF12_CFRULE_SUB_LAST7DAYS:
OSL_ENSURE( nOperator == BIFF12_CFRULE_TIMEOP_LAST7DAYS, "CondFormatRule::importCfRule - unexpected time operator value" );
maModel.mnType = XML_timePeriod;
maModel.mnTimePeriod = XML_last7Days;
break;
case BIFF12_CFRULE_SUB_LASTMONTH:
OSL_ENSURE( nOperator == BIFF12_CFRULE_TIMEOP_LASTMONTH, "CondFormatRule::importCfRule - unexpected time operator value" );
maModel.mnType = XML_timePeriod;
maModel.mnTimePeriod = XML_lastMonth;
break;
case BIFF12_CFRULE_SUB_NEXTMONTH:
OSL_ENSURE( nOperator == BIFF12_CFRULE_TIMEOP_NEXTMONTH, "CondFormatRule::importCfRule - unexpected time operator value" );
maModel.mnType = XML_timePeriod;
maModel.mnTimePeriod = XML_nextMonth;
break;
case BIFF12_CFRULE_SUB_THISWEEK:
OSL_ENSURE( nOperator == BIFF12_CFRULE_TIMEOP_THISWEEK, "CondFormatRule::importCfRule - unexpected time operator value" );
maModel.mnType = XML_timePeriod;
maModel.mnTimePeriod = XML_thisWeek;
break;
case BIFF12_CFRULE_SUB_NEXTWEEK:
OSL_ENSURE( nOperator == BIFF12_CFRULE_TIMEOP_NEXTWEEK, "CondFormatRule::importCfRule - unexpected time operator value" );
maModel.mnType = XML_timePeriod;
maModel.mnTimePeriod = XML_nextWeek;
break;
case BIFF12_CFRULE_SUB_LASTWEEK:
OSL_ENSURE( nOperator == BIFF12_CFRULE_TIMEOP_LASTWEEK, "CondFormatRule::importCfRule - unexpected time operator value" );
maModel.mnType = XML_timePeriod;
maModel.mnTimePeriod = XML_lastWeek;
break;
case BIFF12_CFRULE_SUB_THISMONTH:
OSL_ENSURE( nOperator == BIFF12_CFRULE_TIMEOP_THISMONTH, "CondFormatRule::importCfRule - unexpected time operator value" );
maModel.mnType = XML_timePeriod;
maModel.mnTimePeriod = XML_thisMonth;
break;
case BIFF12_CFRULE_SUB_ABOVEAVERAGE:
OSL_ENSURE( maModel.mbAboveAverage, "CondFormatRule::importCfRule - wrong above-average flag" );
maModel.mnType = XML_aboveAverage;
maModel.mnStdDev = nOperator; // operator field used for standard deviation
maModel.mbAboveAverage = true;
maModel.mbEqualAverage = false; // does not exist as real flag...
break;
case BIFF12_CFRULE_SUB_BELOWAVERAGE:
OSL_ENSURE( !maModel.mbAboveAverage, "CondFormatRule::importCfRule - wrong above-average flag" );
maModel.mnType = XML_aboveAverage;
maModel.mnStdDev = nOperator; // operator field used for standard deviation
maModel.mbAboveAverage = false;
maModel.mbEqualAverage = false; // does not exist as real flag...
break;
case BIFF12_CFRULE_SUB_DUPLICATE:
OSL_ENSURE( nOperator == 0, "CondFormatRule::importCfRule - unexpected operator value" );
maModel.mnType = XML_duplicateValues;
break;
case BIFF12_CFRULE_SUB_EQABOVEAVERAGE:
OSL_ENSURE( maModel.mbAboveAverage, "CondFormatRule::importCfRule - wrong above-average flag" );
maModel.mnType = XML_aboveAverage;
maModel.mnStdDev = nOperator; // operator field used for standard deviation
maModel.mbAboveAverage = true;
maModel.mbEqualAverage = true; // does not exist as real flag...
break;
case BIFF12_CFRULE_SUB_EQBELOWAVERAGE:
OSL_ENSURE( !maModel.mbAboveAverage, "CondFormatRule::importCfRule - wrong above-average flag" );
maModel.mnType = XML_aboveAverage;
maModel.mnStdDev = nOperator; // operator field used for standard deviation
maModel.mbAboveAverage = false;
maModel.mbEqualAverage = true; // does not exist as real flag...
break;
}
break;
case BIFF12_CFRULE_TYPE_COLORSCALE:
OSL_ENSURE( nSubType == BIFF12_CFRULE_SUB_COLORSCALE, "CondFormatRule::importCfRule - rule type/subtype mismatch" );
OSL_ENSURE( nOperator == 0, "CondFormatRule::importCfRule - unexpected operator value" );
maModel.mnType = XML_colorScale;
break;
case BIFF12_CFRULE_TYPE_DATABAR:
OSL_ENSURE( nSubType == BIFF12_CFRULE_SUB_DATABAR, "CondFormatRule::importCfRule - rule type/subtype mismatch" );
OSL_ENSURE( nOperator == 0, "CondFormatRule::importCfRule - unexpected operator value" );
maModel.mnType = XML_dataBar;
break;
case BIFF12_CFRULE_TYPE_TOPTEN:
OSL_ENSURE( nSubType == BIFF12_CFRULE_SUB_TOPTEN, "CondFormatRule::importCfRule - rule type/subtype mismatch" );
maModel.mnType = XML_top10;
maModel.mnRank = nOperator; // operator field used for rank value
break;
case BIFF12_CFRULE_TYPE_ICONSET:
OSL_ENSURE( nSubType == BIFF12_CFRULE_SUB_ICONSET, "CondFormatRule::importCfRule - rule type/subtype mismatch" );
OSL_ENSURE( nOperator == 0, "CondFormatRule::importCfRule - unexpected operator value" );
maModel.mnType = XML_iconSet;
break;
default:
OSL_ENSURE( false, "CondFormatRule::importCfRule - unknown rule type" );
}
}
void CondFormatRule::importCfRule( BiffInputStream& rStrm, sal_Int32 nPriority )
{
sal_uInt8 nType, nOperator;
sal_uInt16 nFmla1Size, nFmla2Size;
sal_uInt32 nFlags;
rStrm >> nType >> nOperator >> nFmla1Size >> nFmla2Size >> nFlags;
rStrm.skip( 2 );
static const sal_Int32 spnTypeIds[] = { XML_TOKEN_INVALID, XML_cellIs, XML_expression };
maModel.mnType = STATIC_ARRAY_SELECT( spnTypeIds, nType, XML_TOKEN_INVALID );
maModel.setBiffOperator( nOperator );
maModel.mnPriority = nPriority;
maModel.mbStopIfTrue = true;
DxfRef xDxf = getStyles().createDxf( &maModel.mnDxfId );
xDxf->importCfRule( rStrm, nFlags );
xDxf->finalizeImport();
// import the formulas
OSL_ENSURE( (nFmla1Size > 0) || (nFmla2Size == 0), "CondFormatRule::importCfRule - missing first formula" );
if( nFmla1Size > 0 )
{
CellAddress aBaseAddr = mrCondFormat.getRanges().getBaseAddress();
ApiTokenSequence aTokens = getFormulaParser().importFormula( aBaseAddr, FORMULATYPE_CONDFORMAT, rStrm, &nFmla1Size );
maModel.maFormulas.push_back( aTokens );
if( nFmla2Size > 0 )
{
aTokens = getFormulaParser().importFormula( aBaseAddr, FORMULATYPE_CONDFORMAT, rStrm, &nFmla2Size );
maModel.maFormulas.push_back( aTokens );
}
}
}
void CondFormatRule::finalizeImport( const Reference< XSheetConditionalEntries >& rxEntries )
{
ConditionOperator eOperator = ConditionOperator_NONE;
/* Replacement formula for unsupported rule types (text comparison rules,
time period rules, cell type rules). The replacement formulas below may
contain several placeholders:
- '#B' will be replaced by the current relative base address (may occur
several times).
- '#R' will be replaced by the entire range list of the conditional
formatting (absolute addresses).
- '#T' will be replaced by the quoted comparison text.
- '#L' will be replaced by the length of the comparison text (from
the 'text' attribute) used in text comparison rules.
- '#K' will be replaced by the rank (from the 'rank' attribute) used in
top-10 rules.
- '#M' will be replaced by the top/bottom flag (from the 'bottom'
attribute) used in the RANK function in top-10 rules.
- '#C' will be replaced by one of the comparison operators <, >, <=, or
>=, according to the 'aboveAverage' and 'equalAverage' flags.
*/
OUString aReplaceFormula;
switch( maModel.mnType )
{
case XML_cellIs:
eOperator = CondFormatBuffer::convertToApiOperator( maModel.mnOperator );
break;
case XML_expression:
eOperator = ConditionOperator_FORMULA;
break;
case XML_containsText:
OSL_ENSURE( maModel.mnOperator == XML_containsText, "CondFormatRule::finalizeImport - unexpected operator" );
aReplaceFormula = CREATE_OUSTRING( "NOT(ISERROR(SEARCH(#T,#B)))" );
break;
case XML_notContainsText:
// note: type XML_notContainsText vs. operator XML_notContains
OSL_ENSURE( maModel.mnOperator == XML_notContains, "CondFormatRule::finalizeImport - unexpected operator" );
aReplaceFormula = CREATE_OUSTRING( "ISERROR(SEARCH(#T,#B))" );
break;
case XML_beginsWith:
OSL_ENSURE( maModel.mnOperator == XML_beginsWith, "CondFormatRule::finalizeImport - unexpected operator" );
aReplaceFormula = CREATE_OUSTRING( "LEFT(#B,#L)=#T" );
break;
case XML_endsWith:
OSL_ENSURE( maModel.mnOperator == XML_endsWith, "CondFormatRule::finalizeImport - unexpected operator" );
aReplaceFormula = CREATE_OUSTRING( "RIGHT(#B,#L)=#T" );
break;
case XML_timePeriod:
switch( maModel.mnTimePeriod )
{
case XML_yesterday:
aReplaceFormula = CREATE_OUSTRING( "FLOOR(#B,1)=TODAY()-1" );
break;
case XML_today:
aReplaceFormula = CREATE_OUSTRING( "FLOOR(#B,1)=TODAY()" );
break;
case XML_tomorrow:
aReplaceFormula = CREATE_OUSTRING( "FLOOR(#B,1)=TODAY()+1" );
break;
case XML_last7Days:
aReplaceFormula = CREATE_OUSTRING( "AND(TODAY()-7<FLOOR(#B,1),FLOOR(#B,1)<=TODAY())" );
break;
case XML_lastWeek:
aReplaceFormula = CREATE_OUSTRING( "AND(TODAY()-WEEKDAY(TODAY())-7<FLOOR(#B,1),FLOOR(#B,1)<=TODAY()-WEEKDAY(TODAY()))" );
break;
case XML_thisWeek:
aReplaceFormula = CREATE_OUSTRING( "AND(TODAY()-WEEKDAY(TODAY())<FLOOR(#B,1),FLOOR(#B,1)<=TODAY()-WEEKDAY(TODAY())+7)" );
break;
case XML_nextWeek:
aReplaceFormula = CREATE_OUSTRING( "AND(TODAY()-WEEKDAY(TODAY())+7<FLOOR(#B,1),FLOOR(#B,1)<=TODAY()-WEEKDAY(TODAY())+14)" );
break;
case XML_lastMonth:
aReplaceFormula = CREATE_OUSTRING( "OR(AND(MONTH(#B)=MONTH(TODAY())-1,YEAR(#B)=YEAR(TODAY())),AND(MONTH(#B)=12,MONTH(TODAY())=1,YEAR(#B)=YEAR(TODAY())-1))" );
break;
case XML_thisMonth:
aReplaceFormula = CREATE_OUSTRING( "AND(MONTH(#B)=MONTH(TODAY()),YEAR(#B)=YEAR(TODAY()))" );
break;
case XML_nextMonth:
aReplaceFormula = CREATE_OUSTRING( "OR(AND(MONTH(#B)=MONTH(TODAY())+1,YEAR(#B)=YEAR(TODAY())),AND(MONTH(#B)=1,MONTH(TODAY())=12,YEAR(#B)=YEAR(TODAY())+1))" );
break;
default:
OSL_ENSURE( false, "CondFormatRule::finalizeImport - unknown time period type" );
}
break;
case XML_containsBlanks:
aReplaceFormula = CREATE_OUSTRING( "LEN(TRIM(#B))=0" );
break;
case XML_notContainsBlanks:
aReplaceFormula = CREATE_OUSTRING( "LEN(TRIM(#B))>0" );
break;
case XML_containsErrors:
aReplaceFormula = CREATE_OUSTRING( "ISERROR(#B)" );
break;
case XML_notContainsErrors:
aReplaceFormula = CREATE_OUSTRING( "NOT(ISERROR(#B))" );
break;
case XML_top10:
if( maModel.mbPercent )
aReplaceFormula = CREATE_OUSTRING( "RANK(#B,#R,#M)/COUNT(#R)<=#K%" );
else
aReplaceFormula = CREATE_OUSTRING( "RANK(#B,#R,#M)<=#K" );
break;
case XML_aboveAverage:
if( maModel.mnStdDev == 0 )
aReplaceFormula = CREATE_OUSTRING( "#B#CAVERAGE(#R)" );
break;
}
if( aReplaceFormula.getLength() > 0 )
{
OUString aAddress, aRanges, aText, aComp;
sal_Int32 nStrPos = aReplaceFormula.getLength();
while( (nStrPos = aReplaceFormula.lastIndexOf( '#', nStrPos )) >= 0 )
{
switch( aReplaceFormula[ nStrPos + 1 ] )
{
case 'B': // current base address
if( aAddress.getLength() == 0 )
aAddress = FormulaProcessorBase::generateAddress2dString( mrCondFormat.getRanges().getBaseAddress(), false );
aReplaceFormula = aReplaceFormula.replaceAt( nStrPos, 2, aAddress );
break;
case 'R': // range list of conditional formatting
if( aRanges.getLength() == 0 )
aRanges = FormulaProcessorBase::generateRangeList2dString( mrCondFormat.getRanges(), true, ',', true );
aReplaceFormula = aReplaceFormula.replaceAt( nStrPos, 2, aRanges );
break;
case 'T': // comparison text
if( aText.getLength() == 0 )
// quote the comparison text, and handle embedded quote characters
aText = FormulaProcessorBase::generateApiString( maModel.maText );
aReplaceFormula = aReplaceFormula.replaceAt( nStrPos, 2, aText );
break;
case 'L': // length of comparison text
aReplaceFormula = aReplaceFormula.replaceAt( nStrPos, 2,
OUString::valueOf( maModel.maText.getLength() ) );
break;
case 'K': // top-10 rank
aReplaceFormula = aReplaceFormula.replaceAt( nStrPos, 2,
OUString::valueOf( maModel.mnRank ) );
break;
case 'M': // top-10 top/bottom flag
aReplaceFormula = aReplaceFormula.replaceAt( nStrPos, 2,
OUString::valueOf( static_cast< sal_Int32 >( maModel.mbBottom ? 1 : 0 ) ) );
break;
case 'C': // average comparison operator
if( aComp.getLength() == 0 )
aComp = maModel.mbAboveAverage ?
(maModel.mbEqualAverage ? CREATE_OUSTRING( ">=" ) : CREATE_OUSTRING( ">" )) :
(maModel.mbEqualAverage ? CREATE_OUSTRING( "<=" ) : CREATE_OUSTRING( "<" ));
aReplaceFormula = aReplaceFormula.replaceAt( nStrPos, 2, aComp );
break;
default:
OSL_ENSURE( false, "CondFormatRule::finalizeImport - unknown placeholder" );
}
}
// set the replacement formula
maModel.maFormulas.clear();
appendFormula( aReplaceFormula );
eOperator = ConditionOperator_FORMULA;
}
if( rxEntries.is() && (eOperator != ConditionOperator_NONE) && !maModel.maFormulas.empty() )
{
::std::vector< PropertyValue > aProps;
// create condition properties
lclAppendProperty( aProps, CREATE_OUSTRING( "Operator" ), eOperator );
lclAppendProperty( aProps, CREATE_OUSTRING( "Formula1" ), maModel.maFormulas[ 0 ] );
if( maModel.maFormulas.size() >= 2 )
lclAppendProperty( aProps, CREATE_OUSTRING( "Formula2" ), maModel.maFormulas[ 1 ] );
// style name for the formatting attributes
OUString aStyleName = getStyles().createDxfStyle( maModel.mnDxfId );
if( aStyleName.getLength() > 0 )
lclAppendProperty( aProps, CREATE_OUSTRING( "StyleName" ), aStyleName );
// append the new rule
try
{
rxEntries->addNew( ContainerHelper::vectorToSequence( aProps ) );
}
catch( Exception& )
{
}
}
}
// ============================================================================
CondFormatModel::CondFormatModel() :
mbPivot( false )
{
}
// ============================================================================
CondFormat::CondFormat( const WorksheetHelper& rHelper ) :
WorksheetHelper( rHelper )
{
}
void CondFormat::importConditionalFormatting( const AttributeList& rAttribs )
{
getAddressConverter().convertToCellRangeList( maModel.maRanges, rAttribs.getString( XML_sqref, OUString() ), getSheetIndex(), true );
maModel.mbPivot = rAttribs.getBool( XML_pivot, false );
}
CondFormatRuleRef CondFormat::importCfRule( const AttributeList& rAttribs )
{
CondFormatRuleRef xRule = createRule();
xRule->importCfRule( rAttribs );
insertRule( xRule );
return xRule;
}
void CondFormat::importCondFormatting( SequenceInputStream& rStrm )
{
BinRangeList aRanges;
rStrm.skip( 8 );
rStrm >> aRanges;
getAddressConverter().convertToCellRangeList( maModel.maRanges, aRanges, getSheetIndex(), true );
}
void CondFormat::importCfRule( SequenceInputStream& rStrm )
{
CondFormatRuleRef xRule = createRule();
xRule->importCfRule( rStrm );
insertRule( xRule );
}
void CondFormat::importCfHeader( BiffInputStream& rStrm )
{
// import the CFHEADER record
sal_uInt16 nRuleCount;
BinRangeList aRanges;
rStrm >> nRuleCount;
rStrm.skip( 10 );
rStrm >> aRanges;
getAddressConverter().convertToCellRangeList( maModel.maRanges, aRanges, getSheetIndex(), true );
// import following list of CFRULE records
for( sal_uInt16 nRule = 0; (nRule < nRuleCount) && (rStrm.getNextRecId() == BIFF_ID_CFRULE) && rStrm.startNextRecord(); ++nRule )
{
CondFormatRuleRef xRule = createRule();
xRule->importCfRule( rStrm, nRule + 1 );
insertRule( xRule );
}
}
void CondFormat::finalizeImport()
{
try
{
Reference< XSheetCellRanges > xRanges( getCellRangeList( maModel.maRanges ), UNO_SET_THROW );
PropertySet aPropSet( xRanges );
Reference< XSheetConditionalEntries > xEntries( aPropSet.getAnyProperty( PROP_ConditionalFormat ), UNO_QUERY_THROW );
// maRules is sorted by rule priority
maRules.forEachMem( &CondFormatRule::finalizeImport, ::boost::cref( xEntries ) );
aPropSet.setProperty( PROP_ConditionalFormat, xEntries );
}
catch( Exception& )
{
}
}
CondFormatRuleRef CondFormat::createRule()
{
return CondFormatRuleRef( new CondFormatRule( *this ) );
}
void CondFormat::insertRule( CondFormatRuleRef xRule )
{
if( xRule.get() && (xRule->getPriority() > 0) )
{
OSL_ENSURE( maRules.find( xRule->getPriority() ) == maRules.end(), "CondFormat::insertRule - multiple rules with equal priority" );
maRules[ xRule->getPriority() ] = xRule;
}
}
// ============================================================================
CondFormatBuffer::CondFormatBuffer( const WorksheetHelper& rHelper ) :
WorksheetHelper( rHelper )
{
}
CondFormatRef CondFormatBuffer::importConditionalFormatting( const AttributeList& rAttribs )
{
CondFormatRef xCondFmt = createCondFormat();
xCondFmt->importConditionalFormatting( rAttribs );
return xCondFmt;
}
CondFormatRef CondFormatBuffer::importCondFormatting( SequenceInputStream& rStrm )
{
CondFormatRef xCondFmt = createCondFormat();
xCondFmt->importCondFormatting( rStrm );
return xCondFmt;
}
void CondFormatBuffer::importCfHeader( BiffInputStream& rStrm )
{
createCondFormat()->importCfHeader( rStrm );
}
void CondFormatBuffer::finalizeImport()
{
maCondFormats.forEachMem( &CondFormat::finalizeImport );
}
ConditionOperator CondFormatBuffer::convertToApiOperator( sal_Int32 nToken )
{
switch( nToken )
{
case XML_between: return ConditionOperator_BETWEEN;
case XML_equal: return ConditionOperator_EQUAL;
case XML_greaterThan: return ConditionOperator_GREATER;
case XML_greaterThanOrEqual: return ConditionOperator_GREATER_EQUAL;
case XML_lessThan: return ConditionOperator_LESS;
case XML_lessThanOrEqual: return ConditionOperator_LESS_EQUAL;
case XML_notBetween: return ConditionOperator_NOT_BETWEEN;
case XML_notEqual: return ConditionOperator_NOT_EQUAL;
}
return ConditionOperator_NONE;
}
// private --------------------------------------------------------------------
CondFormatRef CondFormatBuffer::createCondFormat()
{
CondFormatRef xCondFmt( new CondFormat( *this ) );
maCondFormats.push_back( xCondFmt );
return xCondFmt;
}
// ============================================================================
} // namespace xls
} // namespace oox