LibreOffice Module reportdesign (master) 1
conditionalexpression.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 */
19
21
22#include <o3tl/safeint.hxx>
23#include <osl/diagnose.h>
24
25
26namespace rptui
27{
28
29 // = ConditionalExpression
30
31
33 :m_sPattern( OUString::createFromAscii( _pAsciiPattern ) )
34 {
35 }
36
37
38 OUString ConditionalExpression::assembleExpression( const OUString& _rFieldDataSource, const OUString& _rLHS, const OUString& _rRHS ) const
39 {
40 OUString sExpression( m_sPattern );
41
42 sal_Int32 nPatternIndex = sExpression.indexOf( '$' );
43 while ( nPatternIndex > -1 )
44 {
45 const OUString* pReplace = nullptr;
46 switch ( sExpression[ nPatternIndex + 1 ] )
47 {
48 case '$': pReplace = &_rFieldDataSource; break;
49 case '1': pReplace = &_rLHS; break;
50 case '2': pReplace = &_rRHS; break;
51 default: break;
52 }
53
54 if ( pReplace == nullptr )
55 {
56 OSL_FAIL( "ConditionalExpression::assembleExpression: illegal pattern!" );
57 break;
58 }
59
60 sExpression = sExpression.replaceAt( nPatternIndex, 2, *pReplace );
61 nPatternIndex = sExpression.indexOf( '$', nPatternIndex + pReplace->getLength() + 1 );
62 }
63 return sExpression;
64 }
65
66
67 bool ConditionalExpression::matchExpression( std::u16string_view _rExpression, const std::u16string_view _rFieldDataSource, OUString& _out_rLHS, OUString& _out_rRHS ) const
68 {
69 // if we had regular expression, the matching would be pretty easy ...
70 // just replace $1 and $2 in the pattern with (.*), and then get them with \1 resp. \2.
71 // Unfortunately, we don't have such a regexp engine ...
72
73 // Okay, let's start with replacing all $$ in our pattern with the actual field data source
74 OUString sMatchExpression( m_sPattern );
75 sMatchExpression = sMatchExpression.replaceAll(u"$$", _rFieldDataSource);
76
77 static constexpr OUStringLiteral sLHSPattern( u"$1" );
78 static constexpr OUStringLiteral sRHSPattern( u"$2" );
79 sal_Int32 nLHSIndex( sMatchExpression.indexOf( sLHSPattern ) );
80 sal_Int32 nRHSIndex( sMatchExpression.indexOf( sRHSPattern ) );
81
82 // now we should have at most one occurrence of $1 and $2, resp.
83 OSL_ENSURE( sMatchExpression.indexOf( sLHSPattern, nLHSIndex + 1 ) == -1,
84 "ConditionalExpression::matchExpression: unsupported pattern (more than one LHS occurrence)!" );
85 OSL_ENSURE( sMatchExpression.indexOf( sRHSPattern, nRHSIndex + 1 ) == -1,
86 "ConditionalExpression::matchExpression: unsupported pattern (more than one RHS occurrence)!" );
87 // Also, an LHS must be present, and precede the RHS (if present)
88 OSL_ENSURE( ( nLHSIndex != -1 ) && ( ( nLHSIndex < nRHSIndex ) || ( nRHSIndex == -1 ) ),
89 "ConditionalExpression::matchExpression: no LHS, or an RHS preceding the LHS - this is not supported!" );
90
91 // up to the occurrence of the LHS (which must exist, see above), the two expressions
92 // must be identical
93 if ( sal_Int32(_rExpression.size()) < nLHSIndex )
94 return false;
95 const std::u16string_view sExprPart1( _rExpression.substr( 0, nLHSIndex ) );
96 const std::u16string_view sMatchExprPart1( sMatchExpression.subView( 0, nLHSIndex ) );
97 if ( sExprPart1 != sMatchExprPart1 )
98 // the left-most expression parts do not match
99 return false;
100
101 // after the occurrence of the RHS (or the LHS, if there is no RHS), the two expressions
102 // must be identical, too
103 bool bHaveRHS( nRHSIndex != -1 );
104 sal_Int32 nRightMostIndex( bHaveRHS ? nRHSIndex : nLHSIndex );
105 const std::u16string_view sMatchExprPart3( sMatchExpression.subView( nRightMostIndex + 2 ) );
106 if ( _rExpression.size() < sMatchExprPart3.size() )
107 // the expression is not even long enough to hold the right-most part of the match expression
108 return false;
109 const std::u16string_view sExprPart3( _rExpression.substr( _rExpression.size() - sMatchExprPart3.size() ) );
110 if ( sExprPart3 != sMatchExprPart3 )
111 // the right-most expression parts do not match
112 return false;
113
114 // if we don't have an RHS, we're done
115 if ( !bHaveRHS )
116 {
117 _out_rLHS = _rExpression.substr( sExprPart1.size(), _rExpression.size() - sExprPart1.size() - sExprPart3.size() );
118 return true;
119 }
120
121 // strip the match expression by its right-most and left-most part, and by the placeholders $1 and $2
122 sal_Int32 nMatchExprPart2Start( nLHSIndex + sLHSPattern.getLength() );
123 std::u16string_view sMatchExprPart2 = sMatchExpression.subView(
124 nMatchExprPart2Start,
125 sMatchExpression.getLength() - nMatchExprPart2Start - sMatchExprPart3.size() - 2
126 );
127 // strip the expression by its left-most and right-most part
128 const std::u16string_view sExpression( _rExpression.substr(
129 sExprPart1.size(),
130 _rExpression.size() - sExprPart1.size() - sExprPart3.size()
131 ) );
132
133 size_t nPart2Index = sExpression.find( sMatchExprPart2 );
134 if ( nPart2Index == std::u16string_view::npos )
135 // the "middle" part of the match expression does not exist in the expression at all
136 return false;
137
138 OSL_ENSURE( sExpression.find( sMatchExprPart2, nPart2Index + 1 ) == std::u16string_view::npos,
139 "ConditionalExpression::matchExpression: ambiguous matching!" );
140 // if this fires, then we're lost: The middle part exists two times in the expression,
141 // so we cannot reliably determine what's the LHS and what's the RHS.
142 // Well, the whole mechanism is flawed, anyway:
143 // Encoding the field content in the conditional expression will break as soon
144 // as somebody
145 // - assigns a Data Field to a control
146 // - creates a conditional format expression for the control
147 // - assigns another Data Field to the control
148 // - opens the Conditional Format Dialog, again
149 // Here, at the latest, you can see that we need another mechanism, anyway, which does not
150 // rely on those strange expression building/matching
151
152 _out_rLHS = sExpression.substr( 0, nPart2Index );
153 _out_rRHS = sExpression.substr( nPart2Index + sMatchExprPart2.size() );
154
155 return true;
156 }
157
158
159 // = ConditionalExpressionFactory
160
161
163 {
164 ConditionalExpressions().swap(_out_rCondExp);
165
166 _out_rCondExp[ eBetween ] = std::make_shared<ConditionalExpression>( "AND( ( $$ ) >= ( $1 ); ( $$ ) <= ( $2 ) )" );
167 _out_rCondExp[ eNotBetween ] = std::make_shared<ConditionalExpression>( "NOT( AND( ( $$ ) >= ( $1 ); ( $$ ) <= ( $2 ) ) )" );
168 _out_rCondExp[ eEqualTo ] = std::make_shared<ConditionalExpression>( "( $$ ) = ( $1 )" );
169 _out_rCondExp[ eNotEqualTo ] = std::make_shared<ConditionalExpression>( "( $$ ) <> ( $1 )" );
170 _out_rCondExp[ eGreaterThan ] = std::make_shared<ConditionalExpression>( "( $$ ) > ( $1 )" );
171 _out_rCondExp[ eLessThan ] = std::make_shared<ConditionalExpression>( "( $$ ) < ( $1 )" );
172 _out_rCondExp[ eGreaterOrEqual ] = std::make_shared<ConditionalExpression>( "( $$ ) >= ( $1 )" );
173 _out_rCondExp[ eLessOrEqual ] = std::make_shared<ConditionalExpression>( "( $$ ) <= ( $1 )" );
174
175 return _out_rCondExp.size();
176 }
177
178} // namespace rptui
179
180
181/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
static size_t getKnownConditionalExpressions(ConditionalExpressions &_out_rCondExp)
fills the given map with all ConditionalExpressions which we know
ConditionalExpression(const char *_pAsciiPattern)
OUString assembleExpression(const OUString &_rFieldDataSource, const OUString &_rLHS, const OUString &_rRHS) const
assembles an expression string from a field data source, and one or two operands
bool matchExpression(std::u16string_view _rExpression, const std::u16string_view _rFieldDataSource, OUString &_out_rLHS, OUString &_out_rRHS) const
matches the given expression string to the expression pattern represented by the object
float u
::std::map< ComparisonOperation, PConditionalExpression > ConditionalExpressions