summaryrefslogtreecommitdiffstats
path: root/reportdesign/source/core/misc
diff options
context:
space:
mode:
Diffstat (limited to 'reportdesign/source/core/misc')
-rw-r--r--reportdesign/source/core/misc/conditionalexpression.cxx187
-rw-r--r--reportdesign/source/core/misc/conditionupdater.cxx118
-rw-r--r--reportdesign/source/core/misc/reportformula.cxx124
3 files changed, 429 insertions, 0 deletions
diff --git a/reportdesign/source/core/misc/conditionalexpression.cxx b/reportdesign/source/core/misc/conditionalexpression.cxx
new file mode 100644
index 000000000..0fe6dbb40
--- /dev/null
+++ b/reportdesign/source/core/misc/conditionalexpression.cxx
@@ -0,0 +1,187 @@
+/* -*- 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 <conditionalexpression.hxx>
+
+#include <o3tl/safeint.hxx>
+#include <osl/diagnose.h>
+
+
+namespace rptui
+{
+
+ // = ConditionalExpression
+
+
+ ConditionalExpression::ConditionalExpression( const char* _pAsciiPattern )
+ :m_sPattern( OUString::createFromAscii( _pAsciiPattern ) )
+ {
+ }
+
+
+ OUString ConditionalExpression::assembleExpression( const OUString& _rFieldDataSource, const OUString& _rLHS, const OUString& _rRHS ) const
+ {
+ OUString sExpression( m_sPattern );
+
+ sal_Int32 nPatternIndex = sExpression.indexOf( '$' );
+ while ( nPatternIndex > -1 )
+ {
+ const OUString* pReplace = nullptr;
+ switch ( sExpression[ nPatternIndex + 1 ] )
+ {
+ case '$': pReplace = &_rFieldDataSource; break;
+ case '1': pReplace = &_rLHS; break;
+ case '2': pReplace = &_rRHS; break;
+ default: break;
+ }
+
+ if ( pReplace == nullptr )
+ {
+ OSL_FAIL( "ConditionalExpression::assembleExpression: illegal pattern!" );
+ break;
+ }
+
+ sExpression = sExpression.replaceAt( nPatternIndex, 2, *pReplace );
+ nPatternIndex = sExpression.indexOf( '$', nPatternIndex + pReplace->getLength() + 1 );
+ }
+ return sExpression;
+ }
+
+
+ bool ConditionalExpression::matchExpression( const OUString& _rExpression, const OUString& _rFieldDataSource, OUString& _out_rLHS, OUString& _out_rRHS ) const
+ {
+ // if we had regular expression, the matching would be pretty easy ...
+ // just replace $1 and $2 in the pattern with (.*), and then get them with \1 resp. \2.
+ // Unfortunately, we don't have such a regexp engine ...
+
+ // Okay, let's start with replacing all $$ in our pattern with the actual field data source
+ OUString sMatchExpression( m_sPattern );
+ static const OUStringLiteral sFieldDataPattern( u"$$" );
+ sal_Int32 nIndex( sMatchExpression.indexOf( sFieldDataPattern ) );
+ while ( nIndex != -1 )
+ {
+ sMatchExpression = sMatchExpression.replaceAt( nIndex, sFieldDataPattern.getLength(), _rFieldDataSource );
+ nIndex = sMatchExpression.indexOf( sFieldDataPattern, nIndex + _rFieldDataSource.getLength() );
+ }
+
+ static const OUStringLiteral sLHSPattern( u"$1" );
+ static const OUStringLiteral sRHSPattern( u"$2" );
+ sal_Int32 nLHSIndex( sMatchExpression.indexOf( sLHSPattern ) );
+ sal_Int32 nRHSIndex( sMatchExpression.indexOf( sRHSPattern ) );
+
+ // now we should have at most one occurrence of $1 and $2, resp.
+ OSL_ENSURE( sMatchExpression.indexOf( sLHSPattern, nLHSIndex + 1 ) == -1,
+ "ConditionalExpression::matchExpression: unsupported pattern (more than one LHS occurrence)!" );
+ OSL_ENSURE( sMatchExpression.indexOf( sRHSPattern, nRHSIndex + 1 ) == -1,
+ "ConditionalExpression::matchExpression: unsupported pattern (more than one RHS occurrence)!" );
+ // Also, an LHS must be present, and precede the RHS (if present)
+ OSL_ENSURE( ( nLHSIndex != -1 ) && ( ( nLHSIndex < nRHSIndex ) || ( nRHSIndex == -1 ) ),
+ "ConditionalExpression::matchExpression: no LHS, or an RHS preceding the LHS - this is not supported!" );
+
+ // up to the occurrence of the LHS (which must exist, see above), the two expressions
+ // must be identical
+ if ( _rExpression.getLength() < nLHSIndex )
+ return false;
+ const std::u16string_view sExprPart1( _rExpression.subView( 0, nLHSIndex ) );
+ const std::u16string_view sMatchExprPart1( sMatchExpression.subView( 0, nLHSIndex ) );
+ if ( sExprPart1 != sMatchExprPart1 )
+ // the left-most expression parts do not match
+ return false;
+
+ // after the occurrence of the RHS (or the LHS, if there is no RHS), the two expressions
+ // must be identical, too
+ bool bHaveRHS( nRHSIndex != -1 );
+ sal_Int32 nRightMostIndex( bHaveRHS ? nRHSIndex : nLHSIndex );
+ const std::u16string_view sMatchExprPart3( sMatchExpression.subView( nRightMostIndex + 2 ) );
+ if ( o3tl::make_unsigned(_rExpression.getLength()) < sMatchExprPart3.size() )
+ // the expression is not even long enough to hold the right-most part of the match expression
+ return false;
+ const std::u16string_view sExprPart3( _rExpression.subView( _rExpression.getLength() - sMatchExprPart3.size() ) );
+ if ( sExprPart3 != sMatchExprPart3 )
+ // the right-most expression parts do not match
+ return false;
+
+ // if we don't have an RHS, we're done
+ if ( !bHaveRHS )
+ {
+ _out_rLHS = _rExpression.copy( sExprPart1.size(), _rExpression.getLength() - sExprPart1.size() - sExprPart3.size() );
+ return true;
+ }
+
+ // strip the match expression by its right-most and left-most part, and by the placeholders $1 and $2
+ sal_Int32 nMatchExprPart2Start( nLHSIndex + sLHSPattern.getLength() );
+ std::u16string_view sMatchExprPart2 = sMatchExpression.subView(
+ nMatchExprPart2Start,
+ sMatchExpression.getLength() - nMatchExprPart2Start - sMatchExprPart3.size() - 2
+ );
+ // strip the expression by its left-most and right-most part
+ const std::u16string_view sExpression( _rExpression.subView(
+ sExprPart1.size(),
+ _rExpression.getLength() - sExprPart1.size() - sExprPart3.size()
+ ) );
+
+ size_t nPart2Index = sExpression.find( sMatchExprPart2 );
+ if ( nPart2Index == std::u16string_view::npos )
+ // the "middle" part of the match expression does not exist in the expression at all
+ return false;
+
+ OSL_ENSURE( sExpression.find( sMatchExprPart2, nPart2Index + 1 ) == std::u16string_view::npos,
+ "ConditionalExpression::matchExpression: ambiguous matching!" );
+ // if this fires, then we're lost: The middle part exists two times in the expression,
+ // so we cannot reliably determine what's the LHS and what's the RHS.
+ // Well, the whole mechanism is flawed, anyway:
+ // Encoding the field content in the conditional expression will break as soon
+ // as somebody
+ // - assigns a Data Field to a control
+ // - creates a conditional format expression for the control
+ // - assigns another Data Field to the control
+ // - opens the Conditional Format Dialog, again
+ // Here, at the latest, you can see that we need another mechanism, anyway, which does not
+ // rely on those strange expression building/matching
+
+ _out_rLHS = sExpression.substr( 0, nPart2Index );
+ _out_rRHS = sExpression.substr( nPart2Index + sMatchExprPart2.size() );
+
+ return true;
+ }
+
+
+ // = ConditionalExpressionFactory
+
+
+ size_t ConditionalExpressionFactory::getKnownConditionalExpressions( ConditionalExpressions& _out_rCondExp )
+ {
+ ConditionalExpressions().swap(_out_rCondExp);
+
+ _out_rCondExp[ eBetween ] = std::make_shared<ConditionalExpression>( "AND( ( $$ ) >= ( $1 ); ( $$ ) <= ( $2 ) )" );
+ _out_rCondExp[ eNotBetween ] = std::make_shared<ConditionalExpression>( "NOT( AND( ( $$ ) >= ( $1 ); ( $$ ) <= ( $2 ) ) )" );
+ _out_rCondExp[ eEqualTo ] = std::make_shared<ConditionalExpression>( "( $$ ) = ( $1 )" );
+ _out_rCondExp[ eNotEqualTo ] = std::make_shared<ConditionalExpression>( "( $$ ) <> ( $1 )" );
+ _out_rCondExp[ eGreaterThan ] = std::make_shared<ConditionalExpression>( "( $$ ) > ( $1 )" );
+ _out_rCondExp[ eLessThan ] = std::make_shared<ConditionalExpression>( "( $$ ) < ( $1 )" );
+ _out_rCondExp[ eGreaterOrEqual ] = std::make_shared<ConditionalExpression>( "( $$ ) >= ( $1 )" );
+ _out_rCondExp[ eLessOrEqual ] = std::make_shared<ConditionalExpression>( "( $$ ) <= ( $1 )" );
+
+ return _out_rCondExp.size();
+ }
+
+} // namespace rptui
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/reportdesign/source/core/misc/conditionupdater.cxx b/reportdesign/source/core/misc/conditionupdater.cxx
new file mode 100644
index 000000000..428c3b3e7
--- /dev/null
+++ b/reportdesign/source/core/misc/conditionupdater.cxx
@@ -0,0 +1,118 @@
+/* -*- 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 <conditionupdater.hxx>
+#include <reportformula.hxx>
+
+#include <com/sun/star/report/XFormatCondition.hpp>
+
+#include <tools/diagnose_ex.h>
+
+
+namespace rptui
+{
+
+
+ using ::com::sun::star::beans::PropertyChangeEvent;
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::report::XReportControlModel;
+ using ::com::sun::star::uno::UNO_QUERY;
+ using ::com::sun::star::report::XFormatCondition;
+ using ::com::sun::star::uno::UNO_QUERY_THROW;
+ using ::com::sun::star::uno::Exception;
+
+
+ //= ConditionUpdater
+
+
+ ConditionUpdater::ConditionUpdater()
+ {
+ }
+
+
+ ConditionUpdater::~ConditionUpdater()
+ {
+ }
+
+
+ void ConditionUpdater::notifyPropertyChange( const PropertyChangeEvent& _rEvent )
+ {
+ impl_lateInit_nothrow();
+
+ Reference< XReportControlModel > xRptControlModel( _rEvent.Source, UNO_QUERY );
+ if ( xRptControlModel.is() && _rEvent.PropertyName == "DataField" )
+ {
+ OUString sOldDataSource, sNewDataSource;
+ OSL_VERIFY( _rEvent.OldValue >>= sOldDataSource );
+ OSL_VERIFY( _rEvent.NewValue >>= sNewDataSource );
+ impl_adjustFormatConditions_nothrow( xRptControlModel, sOldDataSource, sNewDataSource );
+ }
+ }
+
+
+ void ConditionUpdater::impl_lateInit_nothrow()
+ {
+ if ( !m_aConditionalExpressions.empty() )
+ return;
+
+ ConditionalExpressionFactory::getKnownConditionalExpressions( m_aConditionalExpressions );
+ }
+
+
+ void ConditionUpdater::impl_adjustFormatConditions_nothrow( const Reference< XReportControlModel >& _rxRptControlModel,
+ const OUString& _rOldDataSource, const OUString& _rNewDataSource )
+ {
+ try
+ {
+ ReportFormula aOldContentFormula( _rOldDataSource );
+ OUString sOldUnprefixed( aOldContentFormula.getBracketedFieldOrExpression() );
+ ReportFormula aNewContentFormula( _rNewDataSource );
+ OUString sNewUnprefixed( aNewContentFormula.getBracketedFieldOrExpression() );
+
+ sal_Int32 nCount( _rxRptControlModel->getCount() );
+ Reference< XFormatCondition > xFormatCondition;
+ OUString sFormulaExpression, sLHS, sRHS;
+ for ( sal_Int32 i=0; i<nCount; ++i )
+ {
+ xFormatCondition.set( _rxRptControlModel->getByIndex( i ), UNO_QUERY_THROW );
+ sFormulaExpression = ReportFormula(xFormatCondition->getFormula()).getExpression();
+
+ for (const auto& rEntry : m_aConditionalExpressions)
+ {
+ if ( !rEntry.second->matchExpression( sFormulaExpression, sOldUnprefixed, sLHS, sRHS ) )
+ continue;
+
+ // the expression matches -> translate it to the new data source of the report control model
+ sFormulaExpression = rEntry.second->assembleExpression( sNewUnprefixed, sLHS, sRHS );
+ ReportFormula aFormula(ReportFormula(ReportFormula::Expression, sFormulaExpression));
+ xFormatCondition->setFormula(aFormula.getCompleteFormula());
+ break;
+ }
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("reportdesign");
+ }
+ }
+
+
+} // namespace rptui
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/reportdesign/source/core/misc/reportformula.cxx b/reportdesign/source/core/misc/reportformula.cxx
new file mode 100644
index 000000000..cd6ae4df5
--- /dev/null
+++ b/reportdesign/source/core/misc/reportformula.cxx
@@ -0,0 +1,124 @@
+/* -*- 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 <reportformula.hxx>
+
+#include <rtl/ustrbuf.hxx>
+
+
+namespace rptui
+{
+ namespace
+ {
+
+ const char sExpressionPrefix[] = "rpt:";
+ const char sFieldPrefix[] = "field:";
+ }
+
+
+ //= ReportFormula
+
+
+ ReportFormula::ReportFormula( const OUString& _rFormula )
+ :m_eType( Invalid )
+ {
+ m_sCompleteFormula = _rFormula;
+
+ // is it an ordinary expression?
+ if ( m_sCompleteFormula.startsWith( sExpressionPrefix, &m_sUndecoratedContent ) )
+ {
+ m_eType = Expression;
+ return;
+ }
+
+ /// does it refer to a field?
+ if ( m_sCompleteFormula.startsWith( sFieldPrefix ) )
+ {
+ sal_Int32 nPrefixLen = strlen(sFieldPrefix);
+ if ( ( m_sCompleteFormula.getLength() >= nPrefixLen + 2 )
+ && ( m_sCompleteFormula[ nPrefixLen ] == '[' )
+ && ( m_sCompleteFormula[ m_sCompleteFormula.getLength() - 1 ] == ']' )
+ )
+ {
+ m_eType = Field;
+ m_sUndecoratedContent = m_sCompleteFormula.copy( nPrefixLen + 1, m_sCompleteFormula.getLength() - nPrefixLen - 2 );
+ return;
+ }
+ }
+
+ m_eType = Invalid;
+ }
+
+
+ ReportFormula::ReportFormula( const BindType _eType, const OUString& _rFieldOrExpression )
+ :m_eType( _eType )
+ {
+ switch ( m_eType )
+ {
+ case Expression:
+ {
+ if ( _rFieldOrExpression.startsWith( sExpressionPrefix ) )
+ m_sCompleteFormula = _rFieldOrExpression;
+ else
+ m_sCompleteFormula = sExpressionPrefix + _rFieldOrExpression;
+ }
+ break;
+
+ case Field:
+ {
+ m_sCompleteFormula = sFieldPrefix + OUString::Concat(u"[") + _rFieldOrExpression + "]";
+ }
+ break;
+ default:
+ OSL_FAIL( "ReportFormula::ReportFormula: illegal bind type!" );
+ return;
+ }
+
+ m_sUndecoratedContent = _rFieldOrExpression;
+ }
+
+ ReportFormula::~ReportFormula()
+ {
+ }
+
+ OUString ReportFormula::getBracketedFieldOrExpression() const
+ {
+ bool bIsField = ( getType() == Field );
+ OUStringBuffer aFieldContent;
+ if ( bIsField )
+ aFieldContent.append( "[" );
+ aFieldContent.append( getUndecoratedContent() );
+ if ( bIsField )
+ aFieldContent.append( "]" );
+
+ return aFieldContent.makeStringAndClear();
+ }
+
+ bool ReportFormula::isValid() const { return getType() != Invalid; }
+
+ OUString ReportFormula::getEqualUndecoratedContent() const
+ {
+ return "=" + getUndecoratedContent();
+ }
+
+
+} // namespace rptui
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */