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 <osl/diagnose.h>
23 
24 
25 namespace rptui
26 {
27 
28  // = ConditionalExpression
29 
30 
31  ConditionalExpression::ConditionalExpression( const char* _pAsciiPattern )
32  :m_sPattern( OUString::createFromAscii( _pAsciiPattern ) )
33  {
34  }
35 
36 
37  OUString ConditionalExpression::assembleExpression( const OUString& _rFieldDataSource, const OUString& _rLHS, const OUString& _rRHS ) const
38  {
39  OUString sExpression( m_sPattern );
40 
41  sal_Int32 nPatternIndex = sExpression.indexOf( '$' );
42  while ( nPatternIndex > -1 )
43  {
44  const OUString* pReplace = nullptr;
45  switch ( sExpression[ nPatternIndex + 1 ] )
46  {
47  case '$': pReplace = &_rFieldDataSource; break;
48  case '1': pReplace = &_rLHS; break;
49  case '2': pReplace = &_rRHS; break;
50  default: break;
51  }
52 
53  if ( pReplace == nullptr )
54  {
55  OSL_FAIL( "ConditionalExpression::assembleExpression: illegal pattern!" );
56  break;
57  }
58 
59  sExpression = sExpression.replaceAt( nPatternIndex, 2, *pReplace );
60  nPatternIndex = sExpression.indexOf( '$', nPatternIndex + pReplace->getLength() + 1 );
61  }
62  return sExpression;
63  }
64 
65 
66  bool ConditionalExpression::matchExpression( const OUString& _rExpression, const OUString& _rFieldDataSource, OUString& _out_rLHS, OUString& _out_rRHS ) const
67  {
68  // if we had regular expression, the matching would be pretty easy ...
69  // just replace $1 and $2 in the pattern with (.*), and then get them with \1 resp. \2.
70  // Unfortunately, we don't have such a regexp engine ...
71 
72  // Okay, let's start with replacing all $$ in our pattern with the actual field data source
73  OUString sMatchExpression( m_sPattern );
74  static const OUStringLiteral sFieldDataPattern( u"$$" );
75  sal_Int32 nIndex( sMatchExpression.indexOf( sFieldDataPattern ) );
76  while ( nIndex != -1 )
77  {
78  sMatchExpression = sMatchExpression.replaceAt( nIndex, sFieldDataPattern.getLength(), _rFieldDataSource );
79  nIndex = sMatchExpression.indexOf( sFieldDataPattern, nIndex + _rFieldDataSource.getLength() );
80  }
81 
82  static const OUStringLiteral sLHSPattern( u"$1" );
83  static const OUStringLiteral sRHSPattern( u"$2" );
84  sal_Int32 nLHSIndex( sMatchExpression.indexOf( sLHSPattern ) );
85  sal_Int32 nRHSIndex( sMatchExpression.indexOf( sRHSPattern ) );
86 
87  // now we should have at most one occurrence of $1 and $2, resp.
88  OSL_ENSURE( sMatchExpression.indexOf( sLHSPattern, nLHSIndex + 1 ) == -1,
89  "ConditionalExpression::matchExpression: unsupported pattern (more than one LHS occurrence)!" );
90  OSL_ENSURE( sMatchExpression.indexOf( sRHSPattern, nRHSIndex + 1 ) == -1,
91  "ConditionalExpression::matchExpression: unsupported pattern (more than one RHS occurrence)!" );
92  // Also, an LHS must be present, and precede the RHS (if present)
93  OSL_ENSURE( ( nLHSIndex != -1 ) && ( ( nLHSIndex < nRHSIndex ) || ( nRHSIndex == -1 ) ),
94  "ConditionalExpression::matchExpression: no LHS, or an RHS preceding the LHS - this is not supported!" );
95 
96  // up to the occurrence of the LHS (which must exist, see above), the two expressions
97  // must be identical
98  if ( _rExpression.getLength() < nLHSIndex )
99  return false;
100  const OUString sExprPart1( _rExpression.copy( 0, nLHSIndex ) );
101  const OUString sMatchExprPart1( sMatchExpression.copy( 0, nLHSIndex ) );
102  if ( sExprPart1 != sMatchExprPart1 )
103  // the left-most expression parts do not match
104  return false;
105 
106  // after the occurrence of the RHS (or the LHS, if there is no RHS), the two expressions
107  // must be identical, too
108  bool bHaveRHS( nRHSIndex != -1 );
109  sal_Int32 nRightMostIndex( bHaveRHS ? nRHSIndex : nLHSIndex );
110  const OUString sMatchExprPart3( sMatchExpression.copy( nRightMostIndex + 2 ) );
111  if ( _rExpression.getLength() < sMatchExprPart3.getLength() )
112  // the expression is not even long enough to hold the right-most part of the match expression
113  return false;
114  const OUString sExprPart3( _rExpression.copy( _rExpression.getLength() - sMatchExprPart3.getLength() ) );
115  if ( sExprPart3 != sMatchExprPart3 )
116  // the right-most expression parts do not match
117  return false;
118 
119  // if we don't have an RHS, we're done
120  if ( !bHaveRHS )
121  {
122  _out_rLHS = _rExpression.copy( sExprPart1.getLength(), _rExpression.getLength() - sExprPart1.getLength() - sExprPart3.getLength() );
123  return true;
124  }
125 
126  // strip the match expression by its right-most and left-most part, and by the placeholders $1 and $2
127  sal_Int32 nMatchExprPart2Start( nLHSIndex + sLHSPattern.getLength() );
128  OUString sMatchExprPart2 = sMatchExpression.copy(
129  nMatchExprPart2Start,
130  sMatchExpression.getLength() - nMatchExprPart2Start - sMatchExprPart3.getLength() - 2
131  );
132  // strip the expression by its left-most and right-most part
133  const OUString sExpression( _rExpression.copy(
134  sExprPart1.getLength(),
135  _rExpression.getLength() - sExprPart1.getLength() - sExprPart3.getLength()
136  ) );
137 
138  sal_Int32 nPart2Index = sExpression.indexOf( sMatchExprPart2 );
139  if ( nPart2Index == -1 )
140  // the "middle" part of the match expression does not exist in the expression at all
141  return false;
142 
143  OSL_ENSURE( sExpression.indexOf( sMatchExprPart2, nPart2Index + 1 ) == -1,
144  "ConditionalExpression::matchExpression: ambiguous matching!" );
145  // if this fires, then we're lost: The middle part exists two times in the expression,
146  // so we cannot reliably determine what's the LHS and what's the RHS.
147  // Well, the whole mechanism is flawed, anyway:
148  // Encoding the field content in the conditional expression will break as soon
149  // as somebody
150  // - assigns a Data Field to a control
151  // - creates a conditional format expression for the control
152  // - assigns another Data Field to the control
153  // - opens the Conditional Format Dialog, again
154  // Here, at the latest, you can see that we need another mechanism, anyway, which does not
155  // rely on those strange expression building/matching
156 
157  _out_rLHS = sExpression.copy( 0, nPart2Index );
158  _out_rRHS = sExpression.copy( nPart2Index + sMatchExprPart2.getLength() );
159 
160  return true;
161  }
162 
163 
164  // = ConditionalExpressionFactory
165 
166 
168  {
169  ConditionalExpressions().swap(_out_rCondExp);
170 
171  _out_rCondExp[ eBetween ] = std::make_shared<ConditionalExpression>( "AND( ( $$ ) >= ( $1 ); ( $$ ) <= ( $2 ) )" );
172  _out_rCondExp[ eNotBetween ] = std::make_shared<ConditionalExpression>( "NOT( AND( ( $$ ) >= ( $1 ); ( $$ ) <= ( $2 ) ) )" );
173  _out_rCondExp[ eEqualTo ] = std::make_shared<ConditionalExpression>( "( $$ ) = ( $1 )" );
174  _out_rCondExp[ eNotEqualTo ] = std::make_shared<ConditionalExpression>( "( $$ ) <> ( $1 )" );
175  _out_rCondExp[ eGreaterThan ] = std::make_shared<ConditionalExpression>( "( $$ ) > ( $1 )" );
176  _out_rCondExp[ eLessThan ] = std::make_shared<ConditionalExpression>( "( $$ ) < ( $1 )" );
177  _out_rCondExp[ eGreaterOrEqual ] = std::make_shared<ConditionalExpression>( "( $$ ) >= ( $1 )" );
178  _out_rCondExp[ eLessOrEqual ] = std::make_shared<ConditionalExpression>( "( $$ ) <= ( $1 )" );
179 
180  return _out_rCondExp.size();
181  }
182 
183 } // namespace rptui
184 
185 
186 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
ConditionalExpression(const char *_pAsciiPattern)
sal_Int32 nIndex
static size_t getKnownConditionalExpressions(ConditionalExpressions &_out_rCondExp)
fills the given map with all ConditionalExpressions which we know
bool matchExpression(const OUString &_rExpression, const OUString &_rFieldDataSource, OUString &_out_rLHS, OUString &_out_rRHS) const
matches the given expression string to the expression pattern represented by the object ...
::std::map< ComparisonOperation, PConditionalExpression > ConditionalExpressions
float u
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