1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 #include "precompiled_reportdesign.hxx"
24 #include "conditionalexpression.hxx"
25 
26 /** === begin UNO includes === **/
27 /** === end UNO includes === **/
28 
29 //........................................................................
30 namespace rptui
31 {
32 //........................................................................
33 
34 	/** === begin UNO using === **/
35 	/** === end UNO using === **/
36 
37     // =============================================================================
38     // = ConditionalExpression
39     // =============================================================================
40     // -----------------------------------------------------------------------------
ConditionalExpression(const sal_Char * _pAsciiPattern)41     ConditionalExpression::ConditionalExpression( const sal_Char* _pAsciiPattern )
42         :m_sPattern( ::rtl::OUString::createFromAscii( _pAsciiPattern ) )
43     {
44     }
45 
46     // -----------------------------------------------------------------------------
assembleExpression(const::rtl::OUString & _rFieldDataSource,const::rtl::OUString & _rLHS,const::rtl::OUString & _rRHS) const47     ::rtl::OUString ConditionalExpression::assembleExpression( const ::rtl::OUString& _rFieldDataSource, const ::rtl::OUString& _rLHS, const ::rtl::OUString& _rRHS ) const
48     {
49         ::rtl::OUString sExpression( m_sPattern );
50 
51         sal_Int32 nPatternIndex = sExpression.indexOf( '$' );
52         while ( nPatternIndex > -1 )
53         {
54             const ::rtl::OUString* pReplace = NULL;
55             switch ( sExpression.getStr()[ nPatternIndex + 1 ] )
56             {
57             case '$': pReplace = &_rFieldDataSource; break;
58             case '1': pReplace = &_rLHS; break;
59             case '2': pReplace = &_rRHS; break;
60             default: break;
61             }
62 
63             if ( pReplace == NULL )
64             {
65                 OSL_ENSURE( false, "ConditionalExpression::assembleExpression: illegal pattern!" );
66                 break;
67             }
68 
69             sExpression = sExpression.replaceAt( nPatternIndex, 2, *pReplace );
70             nPatternIndex = sExpression.indexOf( '$', nPatternIndex + pReplace->getLength() + 1 );
71         }
72         return sExpression;
73     }
74 
75     // -----------------------------------------------------------------------------
matchExpression(const::rtl::OUString & _rExpression,const::rtl::OUString & _rFieldDataSource,::rtl::OUString & _out_rLHS,::rtl::OUString & _out_rRHS) const76     bool ConditionalExpression::matchExpression( const ::rtl::OUString& _rExpression, const ::rtl::OUString& _rFieldDataSource, ::rtl::OUString& _out_rLHS, ::rtl::OUString& _out_rRHS ) const
77     {
78         (void)_rExpression;
79         (void)_rFieldDataSource;
80         (void)_out_rLHS;
81         (void)_out_rRHS;
82 
83         // if we had regular expression, the matching would be pretty easy ...
84         // just replace $1 and $2 in the pattern with (.*), and then get them with \1 resp. \2.
85         // Unfortunately, we don't have such a regexp engine ...
86 
87         // Okay, let's start with replacing all $$ in our pattern with the actual field data source
88         ::rtl::OUString sMatchExpression( m_sPattern );
89         const ::rtl::OUString sFieldDataPattern( RTL_CONSTASCII_USTRINGPARAM( "$$" ) );
90         sal_Int32 nIndex( sMatchExpression.indexOf( sFieldDataPattern ) );
91         while ( nIndex != -1 )
92         {
93             sMatchExpression = sMatchExpression.replaceAt( nIndex, sFieldDataPattern.getLength(), _rFieldDataSource );
94             nIndex = sMatchExpression.indexOf( sFieldDataPattern, nIndex + _rFieldDataSource.getLength() );
95         }
96 
97         const ::rtl::OUString sLHSPattern( RTL_CONSTASCII_USTRINGPARAM( "$1" ) );
98         const ::rtl::OUString sRHSPattern( RTL_CONSTASCII_USTRINGPARAM( "$2" ) );
99         sal_Int32 nLHSIndex( sMatchExpression.indexOf( sLHSPattern ) );
100         sal_Int32 nRHSIndex( sMatchExpression.indexOf( sRHSPattern ) );
101 
102         // now we should have at most one occurrence of $1 and $2, resp.
103         OSL_ENSURE( sMatchExpression.indexOf( sLHSPattern, nLHSIndex + 1 ) == -1,
104             "ConditionalExpression::matchExpression: unsupported pattern (more than one LHS occurrence)!" );
105         OSL_ENSURE( sMatchExpression.indexOf( sRHSPattern, nRHSIndex + 1 ) == -1,
106             "ConditionalExpression::matchExpression: unsupported pattern (more than one RHS occurrence)!" );
107         // Also, an LHS must be present, and precede the RHS (if present)
108         OSL_ENSURE( ( nLHSIndex != -1 ) && ( ( nLHSIndex < nRHSIndex ) || ( nRHSIndex == -1 ) ),
109             "ConditionalExpression::matchExpression: no LHS, or an RHS preceding the LHS - this is not supported!" );
110 
111         // up to the occurrence of the LHS (which must exist, see above), the two expressions
112         // must be identical
113         if ( _rExpression.getLength() < nLHSIndex )
114             return false;
115         const ::rtl::OUString sExprPart1( _rExpression.copy( 0, nLHSIndex ) );
116         const ::rtl::OUString sMatchExprPart1( sMatchExpression.copy( 0, nLHSIndex ) );
117         if ( sExprPart1 != sMatchExprPart1 )
118             // the left-most expression parts do not match
119             return false;
120 
121         // after the occurrence of the RHS (or the LHS, if there is no RHS), the two expressions
122         // must be identical, too
123         bool bHaveRHS( nRHSIndex != -1 );
124         sal_Int32 nRightMostIndex( bHaveRHS ? nRHSIndex : nLHSIndex );
125         const ::rtl::OUString sMatchExprPart3( sMatchExpression.copy( nRightMostIndex + 2 ) );
126         if ( _rExpression.getLength() < sMatchExprPart3.getLength() )
127             // the expression is not even long enough to hold the right-most part of the match expression
128             return false;
129         const ::rtl::OUString sExprPart3( _rExpression.copy( _rExpression.getLength() - sMatchExprPart3.getLength() ) );
130         if ( sExprPart3 != sMatchExprPart3 )
131             // the right-most expression parts do not match
132             return false;
133 
134         // if we don't have an RHS, we're done
135         if ( !bHaveRHS )
136         {
137             _out_rLHS = _rExpression.copy( sExprPart1.getLength(), _rExpression.getLength() - sExprPart1.getLength() - sExprPart3.getLength() );
138             return true;
139         }
140 
141         // strip the match expression by its right-most and left-most part, and by the placeholders $1 and $2
142         sal_Int32 nMatchExprPart2Start( nLHSIndex + sLHSPattern.getLength() );
143         ::rtl::OUString sMatchExprPart2 = sMatchExpression.copy(
144             nMatchExprPart2Start,
145             sMatchExpression.getLength() - nMatchExprPart2Start - sMatchExprPart3.getLength() - 2
146         );
147         // strip the expression by its left-most and right-most part
148         const ::rtl::OUString sExpression( _rExpression.copy(
149             sExprPart1.getLength(),
150             _rExpression.getLength() - sExprPart1.getLength() - sExprPart3.getLength()
151         ) );
152 
153         sal_Int32 nPart2Index = sExpression.indexOf( sMatchExprPart2 );
154         if ( nPart2Index == -1 )
155             // the "middle" part of the match expression does not exist in the expression at all
156             return false;
157 
158         OSL_ENSURE( sExpression.indexOf( sMatchExprPart2, nPart2Index + 1 ) == -1,
159             "ConditionalExpression::matchExpression: ambiguous matching!" );
160             // if this fires, then we're lost: The middle part exists two times in the expression,
161             // so we cannot reliably determine what's the LHS and what's the RHS.
162             // Well, the whole mechanism is flawed, anyway:
163             // Encoding the field content in the conditional expression will break as soon
164             // as somebody
165             // - assigns a Data Field to a control
166             // - creates a conditional format expression for the control
167             // - assigns another Data Field to the control
168             // - opens the Conditional Format Dialog, again
169             // Here, at the latest, you can see that we need another mechanism, anyway, which does not
170             // rely on those strange expression building/matching
171 
172         _out_rLHS = sExpression.copy( 0, nPart2Index );
173         _out_rRHS = sExpression.copy( nPart2Index + sMatchExprPart2.getLength() );
174 
175         return true;
176     }
177 
178     // =============================================================================
179     // = ConditionalExpressionFactory
180     // =============================================================================
181     // -----------------------------------------------------------------------------
getKnownConditionalExpressions(ConditionalExpressions & _out_rCondExp)182     size_t ConditionalExpressionFactory::getKnownConditionalExpressions( ConditionalExpressions& _out_rCondExp )
183     {
184         ConditionalExpressions aEmpty;
185         _out_rCondExp.swap( aEmpty );
186 
187         _out_rCondExp[ eBetween ]        = PConditionalExpression( new ConditionalExpression( "AND( ( $$ ) >= ( $1 ); ( $$ ) <= ( $2 ) )" ) );
188         _out_rCondExp[ eNotBetween ]     = PConditionalExpression( new ConditionalExpression( "NOT( AND( ( $$ ) >= ( $1 ); ( $$ ) <= ( $2 ) ) )" ) );
189         _out_rCondExp[ eEqualTo ]        = PConditionalExpression( new ConditionalExpression( "( $$ ) = ( $1 )" ) );
190         _out_rCondExp[ eNotEqualTo ]     = PConditionalExpression( new ConditionalExpression( "( $$ ) <> ( $1 )" ) );
191         _out_rCondExp[ eGreaterThan ]    = PConditionalExpression( new ConditionalExpression( "( $$ ) > ( $1 )" ) );
192         _out_rCondExp[ eLessThan ]       = PConditionalExpression( new ConditionalExpression( "( $$ ) < ( $1 )" ) );
193         _out_rCondExp[ eGreaterOrEqual ] = PConditionalExpression( new ConditionalExpression( "( $$ ) >= ( $1 )" ) );
194         _out_rCondExp[ eLessOrEqual ]    = PConditionalExpression( new ConditionalExpression( "( $$ ) <= ( $1 )" ) );
195 
196         return _out_rCondExp.size();
197     }
198 //........................................................................
199 } // namespace rptui
200 //........................................................................
201