/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* 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 .
*/
#include "xmlcvali.hxx"
#include "xmlimprt.hxx"
#include "xmlconti.hxx"
#include <document.hxx>
#include "XMLConverter.hxx"
#include <xmloff/xmltoken.hxx>
#include <xmloff/xmlnamespace.hxx>
#include <xmloff/XMLEventsImportContext.hxx>
#include <com/sun/star/sheet/TableValidationVisibility.hpp>
using namespace com::sun::star;
using namespace xmloff::token;
using namespace ::formula;
namespace {
class ScXMLContentValidationContext : public ScXMLImportContext
{
OUString sName;
OUString sHelpTitle;
OUString sHelpMessage;
OUString sErrorTitle;
OUString sErrorMessage;
OUString sErrorMessageType;
OUString sBaseCellAddress;
OUString sCondition;
sal_Int16 nShowList;
bool bAllowEmptyCell;
bool bIsCaseSensitive;
bool bDisplayHelp;
bool bDisplayError;
rtl::Reference<XMLEventsImportContext> xEventContext;
css::sheet::ValidationAlertStyle GetAlertStyle() const;
void SetFormula( OUString& rFormula, OUString& rFormulaNmsp, FormulaGrammar::Grammar& reGrammar,
const OUString& rCondition, const OUString& rGlobNmsp, FormulaGrammar::Grammar eGlobGrammar, bool bHasNmsp ) const;
void GetCondition( ScMyImportValidation& rValidation ) const;
public:
ScXMLContentValidationContext( ScXMLImport& rImport,
const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList );
virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
void SetHelpMessage(const OUString& sTitle, const OUString& sMessage, const bool bDisplay);
void SetErrorMessage(const OUString& sTitle, const OUString& sMessage, const OUString& sMessageType, const bool bDisplay);
void SetErrorMacro(const bool bExecute);
};
class ScXMLHelpMessageContext : public ScXMLImportContext
{
OUString sTitle;
OUStringBuffer sMessage;
sal_Int32 nParagraphCount;
bool bDisplay;
ScXMLContentValidationContext* pValidationContext;
public:
ScXMLHelpMessageContext( ScXMLImport& rImport,
const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
ScXMLContentValidationContext* pValidationContext);
virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
sal_Int32 nElement,
const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
};
class ScXMLErrorMessageContext : public ScXMLImportContext
{
OUString sTitle;
OUStringBuffer sMessage;
OUString sMessageType;
sal_Int32 nParagraphCount;
bool bDisplay;
ScXMLContentValidationContext* pValidationContext;
public:
ScXMLErrorMessageContext( ScXMLImport& rImport,
const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
ScXMLContentValidationContext* pValidationContext);
virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
sal_Int32 nElement,
const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
};
class ScXMLErrorMacroContext : public ScXMLImportContext
{
bool bExecute;
ScXMLContentValidationContext* pValidationContext;
public:
ScXMLErrorMacroContext( ScXMLImport& rImport,
const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
ScXMLContentValidationContext* pValidationContext);
virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
sal_Int32 nElement,
const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
};
}
ScXMLContentValidationsContext::ScXMLContentValidationsContext( ScXMLImport& rImport ) :
ScXMLImportContext( rImport )
{
// here are no attributes
}
ScXMLContentValidationsContext::~ScXMLContentValidationsContext()
{
}
uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLContentValidationsContext::createFastChildContext(
sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
{
SvXMLImportContext *pContext = nullptr;
sax_fastparser::FastAttributeList *pAttribList =
&sax_fastparser::castToFastAttributeList( xAttrList );
switch (nElement)
{
case XML_ELEMENT( TABLE, XML_CONTENT_VALIDATION ):
pContext = new ScXMLContentValidationContext( GetScImport(), pAttribList );
break;
}
return pContext;
}
ScXMLContentValidationContext::ScXMLContentValidationContext( ScXMLImport& rImport,
const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList ) :
ScXMLImportContext( rImport ),
nShowList(sheet::TableValidationVisibility::UNSORTED),
bAllowEmptyCell(true),
bIsCaseSensitive(false),
bDisplayHelp(false),
bDisplayError(false)
{
if ( !rAttrList.is() )
return;
for (auto &aIter : *rAttrList)
{
switch (aIter.getToken())
{
case XML_ELEMENT( TABLE, XML_NAME ):
sName = aIter.toString();
break;
case XML_ELEMENT( TABLE, XML_CONDITION ):
sCondition = aIter.toString();
break;
case XML_ELEMENT( TABLE, XML_BASE_CELL_ADDRESS ):
sBaseCellAddress = aIter.toString();
break;
case XML_ELEMENT( TABLE, XML_ALLOW_EMPTY_CELL ):
if (IsXMLToken(aIter, XML_FALSE))
bAllowEmptyCell = false;
break;
case XML_ELEMENT( TABLE, XML_CASE_SENSITIVE ):
if (IsXMLToken(aIter, XML_TRUE))
bIsCaseSensitive = true;
break;
case XML_ELEMENT( TABLE, XML_DISPLAY_LIST ):
if (IsXMLToken(aIter, XML_NO))
{
nShowList = sheet::TableValidationVisibility::INVISIBLE;
}
else if (IsXMLToken(aIter, XML_UNSORTED))
{
nShowList = sheet::TableValidationVisibility::UNSORTED;
}
else if (IsXMLToken(aIter, XML_SORT_ASCENDING))
{
nShowList = sheet::TableValidationVisibility::SORTEDASCENDING;
}
else if (IsXMLToken(aIter, XML_SORTED_ASCENDING))
{
// Read old wrong value, fdo#72548
nShowList = sheet::TableValidationVisibility::SORTEDASCENDING;
}
break;
}
}
}
uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLContentValidationContext::createFastChildContext(
sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
{
SvXMLImportContext *pContext = nullptr;
sax_fastparser::FastAttributeList *pAttribList =
&sax_fastparser::castToFastAttributeList( xAttrList );
switch (nElement)
{
case XML_ELEMENT( TABLE, XML_HELP_MESSAGE ):
pContext = new ScXMLHelpMessageContext( GetScImport(), pAttribList, this);
break;
case XML_ELEMENT( TABLE, XML_ERROR_MESSAGE ):
pContext = new ScXMLErrorMessageContext( GetScImport(), pAttribList, this);
break;
case XML_ELEMENT( TABLE, XML_ERROR_MACRO ):
pContext = new ScXMLErrorMacroContext( GetScImport(), pAttribList, this);
break;
case XML_ELEMENT(OFFICE, XML_EVENT_LISTENERS):
xEventContext = new XMLEventsImportContext( GetImport() );
pContext = xEventContext.get();
}
return pContext;
}
sheet::ValidationAlertStyle ScXMLContentValidationContext::GetAlertStyle() const
{
if (IsXMLToken(sErrorMessageType, XML_MACRO))
return sheet::ValidationAlertStyle_MACRO;
if (IsXMLToken(sErrorMessageType, XML_STOP))
return sheet::ValidationAlertStyle_STOP;
if (IsXMLToken(sErrorMessageType, XML_WARNING))
return sheet::ValidationAlertStyle_WARNING;
if (IsXMLToken(sErrorMessageType, XML_INFORMATION))
return sheet::ValidationAlertStyle_INFO;
// default for unknown
return sheet::ValidationAlertStyle_STOP;
}
void ScXMLContentValidationContext::SetFormula( OUString& rFormula, OUString& rFormulaNmsp, FormulaGrammar::Grammar& reGrammar,
const OUString& rCondition, const OUString& rGlobNmsp, FormulaGrammar::Grammar eGlobGrammar, bool bHasNmsp ) const
{
reGrammar = FormulaGrammar::GRAM_UNSPECIFIED;
if( bHasNmsp )
{
// the entire attribute contains a namespace: internal namespace not allowed
rFormula = rCondition;
rFormulaNmsp = rGlobNmsp;
reGrammar = eGlobGrammar;
}
else
{
// the attribute does not contain a namespace: try to find a namespace of an external grammar
GetScImport().ExtractFormulaNamespaceGrammar( rFormula, rFormulaNmsp, reGrammar, rCondition, true );
if( reGrammar != FormulaGrammar::GRAM_EXTERNAL )
reGrammar = eGlobGrammar;
}
}
void ScXMLContentValidationContext::GetCondition( ScMyImportValidation& rValidation ) const
{
rValidation.aValidationType = sheet::ValidationType_ANY; // default if no condition is given
rValidation.aOperator = sheet::ConditionOperator_NONE;
if( sCondition.isEmpty() )
return;
// extract leading namespace from condition string
OUString aCondition, aConditionNmsp;
FormulaGrammar::Grammar eGrammar = FormulaGrammar::GRAM_UNSPECIFIED;
GetScImport().ExtractFormulaNamespaceGrammar( aCondition, aConditionNmsp, eGrammar, sCondition );
bool bHasNmsp = aCondition.getLength() < sCondition.getLength();
// parse a condition from the attribute string
ScXMLConditionParseResult aParseResult;
ScXMLConditionHelper::parseCondition( aParseResult, aCondition, 0 );
/* Check the result. A valid value in aParseResult.meToken implies
that the other members of aParseResult are filled with valid data
for that token. */
bool bSecondaryPart = false;
switch( aParseResult.meToken )
{
case XML_COND_TEXTLENGTH: // condition is 'cell-content-text-length()<operator><expression>'
case XML_COND_TEXTLENGTH_ISBETWEEN: // condition is 'cell-content-text-length-is-between(<expression1>,<expression2>)'
case XML_COND_TEXTLENGTH_ISNOTBETWEEN: // condition is 'cell-content-text-length-is-not-between(<expression1>,<expression2>)'
case XML_COND_ISINLIST: // condition is 'cell-content-is-in-list(<expression>)'
case XML_COND_ISTRUEFORMULA: // condition is 'is-true-formula(<expression>)'
rValidation.aValidationType = aParseResult.meValidation;
rValidation.aOperator = aParseResult.meOperator;
break;
case XML_COND_ISWHOLENUMBER: // condition is 'cell-content-is-whole-number() and <condition>'
case XML_COND_ISDECIMALNUMBER: // condition is 'cell-content-is-decimal-number() and <condition>'
case XML_COND_ISDATE: // condition is 'cell-content-is-date() and <condition>'
case XML_COND_ISTIME: // condition is 'cell-content-is-time() and <condition>'
rValidation.aValidationType = aParseResult.meValidation;
bSecondaryPart = true;
break;
default:; // unacceptable or unknown condition
}
/* Parse the following 'and <condition>' part of some conditions. This
updates the members of aParseResult that will contain the operands
and comparison operator then. */
if( bSecondaryPart )
{
ScXMLConditionHelper::parseCondition( aParseResult, aCondition, aParseResult.mnEndIndex );
if( aParseResult.meToken == XML_COND_AND )
{
ScXMLConditionHelper::parseCondition( aParseResult, aCondition, aParseResult.mnEndIndex );
switch( aParseResult.meToken )
{
case XML_COND_CELLCONTENT: // condition is 'and cell-content()<operator><expression>'
case XML_COND_ISBETWEEN: // condition is 'and cell-content-is-between(<expression1>,<expression2>)'
case XML_COND_ISNOTBETWEEN: // condition is 'and cell-content-is-not-between(<expression1>,<expression2>)'
rValidation.aOperator = aParseResult.meOperator;
break;
default:; // unacceptable or unknown condition
}
}
}
// a validation type (date, integer) without a condition isn't possible
if( rValidation.aOperator == sheet::ConditionOperator_NONE )
rValidation.aValidationType = sheet::ValidationType_ANY;
// parse the formulas
if( rValidation.aValidationType != sheet::ValidationType_ANY )
{
SetFormula( rValidation.sFormula1, rValidation.sFormulaNmsp1, rValidation.eGrammar1,
aParseResult.maOperand1, aConditionNmsp, eGrammar, bHasNmsp );
SetFormula( rValidation.sFormula2, rValidation.sFormulaNmsp2, rValidation.eGrammar2,
aParseResult.maOperand2, aConditionNmsp, eGrammar, bHasNmsp );
}
}
void SAL_CALL ScXMLContentValidationContext::endFastElement( sal_Int32 /*nElement*/ )
{
// #i36650# event-listeners element moved up one level
if (xEventContext.is())
{
uno::Sequence<beans::PropertyValue> aValues;
xEventContext->GetEventSequence( u"OnError"_ustr, aValues );
auto pValue = std::find_if(std::cbegin(aValues), std::cend(aValues),
[](const beans::PropertyValue& rValue) {
return rValue.Name == "MacroName" || rValue.Name == "Script"; });
if (pValue != std::cend(aValues))
pValue->Value >>= sErrorTitle;
}
ScMyImportValidation aValidation;
aValidation.eGrammar1 = aValidation.eGrammar2 = GetScImport().GetDocument()->GetStorageGrammar();
aValidation.sName = sName;
aValidation.sBaseCellAddress = sBaseCellAddress;
aValidation.sInputTitle = sHelpTitle;
aValidation.sInputMessage = sHelpMessage;
aValidation.sErrorTitle = sErrorTitle;
aValidation.sErrorMessage = sErrorMessage;
GetCondition( aValidation );
aValidation.aAlertStyle = GetAlertStyle();
aValidation.bShowErrorMessage = bDisplayError;
aValidation.bShowInputMessage = bDisplayHelp;
aValidation.bIgnoreBlanks = bAllowEmptyCell;
aValidation.bCaseSensitive = bIsCaseSensitive;
aValidation.nShowList = nShowList;
GetScImport().AddValidation(aValidation);
}
void ScXMLContentValidationContext::SetHelpMessage(const OUString& sTitle, const OUString& sMessage, const bool bDisplay)
{
sHelpTitle = sTitle;
sHelpMessage = sMessage;
bDisplayHelp = bDisplay;
}
void ScXMLContentValidationContext::SetErrorMessage(const OUString& sTitle, const OUString& sMessage,
const OUString& sMessageType, const bool bDisplay)
{
sErrorTitle = sTitle;
sErrorMessage = sMessage;
sErrorMessageType = sMessageType;
bDisplayError = bDisplay;
}
void ScXMLContentValidationContext::SetErrorMacro(const bool bExecute)
{
sErrorMessageType = "macro";
bDisplayError = bExecute;
}
ScXMLHelpMessageContext::ScXMLHelpMessageContext( ScXMLImport& rImport,
const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
ScXMLContentValidationContext* pTempValidationContext) :
ScXMLImportContext( rImport ),
nParagraphCount(0),
bDisplay(false)
{
pValidationContext = pTempValidationContext;
if ( !rAttrList.is() )
return;
for (auto &aIter : *rAttrList)
{
switch (aIter.getToken())
{
case XML_ELEMENT( TABLE, XML_TITLE ):
sTitle = aIter.toString();
break;
case XML_ELEMENT( TABLE, XML_DISPLAY ):
bDisplay = IsXMLToken(aIter, XML_TRUE);
break;
}
}
}
css::uno::Reference< css::xml::sax::XFastContextHandler > ScXMLHelpMessageContext::createFastChildContext(
sal_Int32 nElement,
const css::uno::Reference< css::xml::sax::XFastAttributeList >& /*xAttrList*/ )
{
SvXMLImportContext *pContext = nullptr;
switch( nElement )
{
case XML_ELEMENT(TEXT, XML_P):
{
if(nParagraphCount)
sMessage.append('\n');
++nParagraphCount;
pContext = new ScXMLContentContext( GetScImport(), sMessage );
}
break;
}
return pContext;
}
void SAL_CALL ScXMLHelpMessageContext::endFastElement( sal_Int32 /*nElement*/ )
{
pValidationContext->SetHelpMessage(sTitle, sMessage.makeStringAndClear(), bDisplay);
}
ScXMLErrorMessageContext::ScXMLErrorMessageContext( ScXMLImport& rImport,
const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
ScXMLContentValidationContext* pTempValidationContext) :
ScXMLImportContext( rImport ),
nParagraphCount(0),
bDisplay(false)
{
pValidationContext = pTempValidationContext;
if ( !rAttrList.is() )
return;
for (auto &aIter : *rAttrList)
{
switch (aIter.getToken())
{
case XML_ELEMENT( TABLE, XML_TITLE ):
sTitle = aIter.toString();
break;
case XML_ELEMENT( TABLE, XML_MESSAGE_TYPE ):
sMessageType = aIter.toString();
break;
case XML_ELEMENT( TABLE, XML_DISPLAY ):
bDisplay = IsXMLToken(aIter, XML_TRUE);
break;
}
}
}
css::uno::Reference< css::xml::sax::XFastContextHandler > ScXMLErrorMessageContext::createFastChildContext(
sal_Int32 nElement,
const css::uno::Reference< css::xml::sax::XFastAttributeList >& /*xAttrList*/ )
{
SvXMLImportContext *pContext = nullptr;
switch( nElement )
{
case XML_ELEMENT(TEXT, XML_P):
{
if(nParagraphCount)
sMessage.append('\n');
++nParagraphCount;
pContext = new ScXMLContentContext( GetScImport(), sMessage);
}
break;
}
return pContext;
}
void SAL_CALL ScXMLErrorMessageContext::endFastElement( sal_Int32 /*nElement*/ )
{
pValidationContext->SetErrorMessage(sTitle, sMessage.makeStringAndClear(), sMessageType, bDisplay);
}
ScXMLErrorMacroContext::ScXMLErrorMacroContext( ScXMLImport& rImport,
const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
ScXMLContentValidationContext* pTempValidationContext) :
ScXMLImportContext( rImport ),
bExecute(false)
{
pValidationContext = pTempValidationContext;
if ( !rAttrList.is() )
return;
for (auto &aIter : *rAttrList)
{
switch (aIter.getToken())
{
case XML_ELEMENT( TABLE, XML_NAME ):
break;
case XML_ELEMENT( TABLE, XML_EXECUTE ):
bExecute = IsXMLToken(aIter, XML_TRUE);
break;
}
}
}
css::uno::Reference< css::xml::sax::XFastContextHandler > ScXMLErrorMacroContext::createFastChildContext(
sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& /*xAttrList*/ )
{
SvXMLImportContext *pContext = nullptr;
if (nElement == XML_ELEMENT(SCRIPT, XML_EVENTS))
{
pContext = new XMLEventsImportContext(GetImport());
}
return pContext;
}
void SAL_CALL ScXMLErrorMacroContext::endFastElement( sal_Int32 /*nElement*/ )
{
pValidationContext->SetErrorMacro( bExecute );
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'append' is required to be utilized.