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 
20 #include "XMLConverter.hxx"
21 #include <com/sun/star/util/DateTime.hpp>
22 #include <com/sun/star/sheet/GeneralFunction2.hpp>
23 #include <comphelper/servicehelper.hxx>
24 #include <tools/datetime.hxx>
25 #include <sax/tools/converter.hxx>
26 #include <xmloff/xmltoken.hxx>
27 #include <rangeutl.hxx>
28 #include <docuno.hxx>
29 #include <generalfunction.hxx>
30 
31 using namespace ::com::sun::star;
32 using namespace xmloff::token;
33 
GetScDocument(const uno::Reference<frame::XModel> & xModel)34 ScDocument* ScXMLConverter::GetScDocument( const uno::Reference< frame::XModel >& xModel )
35 {
36     if (xModel.is())
37     {
38         ScModelObj* pDocObj = comphelper::getUnoTunnelImplementation<ScModelObj>( xModel );
39         return pDocObj ? pDocObj->GetDocument() : nullptr;
40     }
41     return nullptr;
42 }
43 
GetFunctionFromString(std::u16string_view sFunction)44 sheet::GeneralFunction ScXMLConverter::GetFunctionFromString( std::u16string_view sFunction )
45 {
46     if( IsXMLToken(sFunction, XML_SUM ) )
47         return sheet::GeneralFunction_SUM;
48     if( IsXMLToken(sFunction, XML_AUTO ) )
49         return sheet::GeneralFunction_AUTO;
50     if( IsXMLToken(sFunction, XML_COUNT ) )
51         return sheet::GeneralFunction_COUNT;
52     if( IsXMLToken(sFunction, XML_COUNTNUMS ) )
53         return sheet::GeneralFunction_COUNTNUMS;
54     if( IsXMLToken(sFunction, XML_PRODUCT ) )
55         return sheet::GeneralFunction_PRODUCT;
56     if( IsXMLToken(sFunction, XML_AVERAGE ) )
57         return sheet::GeneralFunction_AVERAGE;
58     if( IsXMLToken(sFunction, XML_MAX ) )
59         return sheet::GeneralFunction_MAX;
60     if( IsXMLToken(sFunction, XML_MIN ) )
61         return sheet::GeneralFunction_MIN;
62     if( IsXMLToken(sFunction, XML_STDEV ) )
63         return sheet::GeneralFunction_STDEV;
64     if( IsXMLToken(sFunction, XML_STDEVP ) )
65         return sheet::GeneralFunction_STDEVP;
66     if( IsXMLToken(sFunction, XML_VAR ) )
67         return sheet::GeneralFunction_VAR;
68     if( IsXMLToken(sFunction, XML_VARP ) )
69         return sheet::GeneralFunction_VARP;
70     return sheet::GeneralFunction_NONE;
71 }
72 
GetFunctionFromString2(std::u16string_view sFunction)73 ScGeneralFunction ScXMLConverter::GetFunctionFromString2( std::u16string_view sFunction )
74 {
75     if( IsXMLToken(sFunction, XML_SUM ) )
76         return ScGeneralFunction::SUM;
77     if( IsXMLToken(sFunction, XML_AUTO ) )
78         return ScGeneralFunction::AUTO;
79     if( IsXMLToken(sFunction, XML_COUNT ) )
80         return ScGeneralFunction::COUNT;
81     if( IsXMLToken(sFunction, XML_COUNTNUMS ) )
82         return ScGeneralFunction::COUNTNUMS;
83     if( IsXMLToken(sFunction, XML_PRODUCT ) )
84         return ScGeneralFunction::PRODUCT;
85     if( IsXMLToken(sFunction, XML_AVERAGE ) )
86         return ScGeneralFunction::AVERAGE;
87     if( IsXMLToken(sFunction, XML_MEDIAN ) )
88         return ScGeneralFunction::MEDIAN;
89     if( IsXMLToken(sFunction, XML_MAX ) )
90         return ScGeneralFunction::MAX;
91     if( IsXMLToken(sFunction, XML_MIN ) )
92         return ScGeneralFunction::MIN;
93     if( IsXMLToken(sFunction, XML_STDEV ) )
94         return ScGeneralFunction::STDEV;
95     if( IsXMLToken(sFunction, XML_STDEVP ) )
96         return ScGeneralFunction::STDEVP;
97     if( IsXMLToken(sFunction, XML_VAR ) )
98         return ScGeneralFunction::VAR;
99     if( IsXMLToken(sFunction, XML_VARP ) )
100         return ScGeneralFunction::VARP;
101     return ScGeneralFunction::NONE;
102 }
103 
GetSubTotalFuncFromString(std::u16string_view sFunction)104 ScSubTotalFunc ScXMLConverter::GetSubTotalFuncFromString( std::u16string_view sFunction )
105 {
106     if( IsXMLToken(sFunction, XML_SUM ) )
107         return SUBTOTAL_FUNC_SUM;
108     if( IsXMLToken(sFunction, XML_COUNT ) )
109         return SUBTOTAL_FUNC_CNT;
110     if( IsXMLToken(sFunction, XML_COUNTNUMS ) )
111         return SUBTOTAL_FUNC_CNT2;
112     if( IsXMLToken(sFunction, XML_PRODUCT ) )
113         return SUBTOTAL_FUNC_PROD;
114     if( IsXMLToken(sFunction, XML_AVERAGE ) )
115         return SUBTOTAL_FUNC_AVE;
116     if( IsXMLToken(sFunction, XML_MEDIAN ) )
117         return SUBTOTAL_FUNC_MED;
118     if( IsXMLToken(sFunction, XML_MAX ) )
119         return SUBTOTAL_FUNC_MAX;
120     if( IsXMLToken(sFunction, XML_MIN ) )
121         return SUBTOTAL_FUNC_MIN;
122     if( IsXMLToken(sFunction, XML_STDEV ) )
123         return SUBTOTAL_FUNC_STD;
124     if( IsXMLToken(sFunction, XML_STDEVP ) )
125         return SUBTOTAL_FUNC_STDP;
126     if( IsXMLToken(sFunction, XML_VAR ) )
127         return SUBTOTAL_FUNC_VAR;
128     if( IsXMLToken(sFunction, XML_VARP ) )
129         return SUBTOTAL_FUNC_VARP;
130     return SUBTOTAL_FUNC_NONE;
131 }
132 
GetStringFromFunction(OUString & rString,sal_Int16 eFunction)133 void ScXMLConverter::GetStringFromFunction(
134         OUString& rString,
135         sal_Int16 eFunction )
136 {
137     OUString sFuncStr;
138     switch( eFunction )
139     {
140         case sheet::GeneralFunction2::AUTO:       sFuncStr = GetXMLToken( XML_AUTO );         break;
141         case sheet::GeneralFunction2::AVERAGE:    sFuncStr = GetXMLToken( XML_AVERAGE );      break;
142         case sheet::GeneralFunction2::MEDIAN:     sFuncStr = GetXMLToken( XML_MEDIAN );       break;
143         case sheet::GeneralFunction2::COUNT:      sFuncStr = GetXMLToken( XML_COUNT );        break;
144         case sheet::GeneralFunction2::COUNTNUMS:  sFuncStr = GetXMLToken( XML_COUNTNUMS );    break;
145         case sheet::GeneralFunction2::MAX:        sFuncStr = GetXMLToken( XML_MAX );          break;
146         case sheet::GeneralFunction2::MIN:        sFuncStr = GetXMLToken( XML_MIN );          break;
147         case sheet::GeneralFunction2::NONE:       sFuncStr = GetXMLToken( XML_NONE );         break;
148         case sheet::GeneralFunction2::PRODUCT:    sFuncStr = GetXMLToken( XML_PRODUCT );      break;
149         case sheet::GeneralFunction2::STDEV:      sFuncStr = GetXMLToken( XML_STDEV );        break;
150         case sheet::GeneralFunction2::STDEVP:     sFuncStr = GetXMLToken( XML_STDEVP );       break;
151         case sheet::GeneralFunction2::SUM:        sFuncStr = GetXMLToken( XML_SUM );          break;
152         case sheet::GeneralFunction2::VAR:        sFuncStr = GetXMLToken( XML_VAR );          break;
153         case sheet::GeneralFunction2::VARP:       sFuncStr = GetXMLToken( XML_VARP );         break;
154         default:
155         {
156             assert(false);
157         }
158     }
159     ScRangeStringConverter::AssignString( rString, sFuncStr, false );
160 }
161 
GetStringFromFunction(OUString & rString,const ScSubTotalFunc eFunction)162 void ScXMLConverter::GetStringFromFunction(
163         OUString& rString,
164         const ScSubTotalFunc eFunction )
165 {
166     OUString sFuncStr;
167     switch( eFunction )
168     {
169         case SUBTOTAL_FUNC_AVE:     sFuncStr = GetXMLToken( XML_AVERAGE );      break;
170         case SUBTOTAL_FUNC_MED:     sFuncStr = GetXMLToken( XML_MEDIAN );       break;
171         case SUBTOTAL_FUNC_CNT:     sFuncStr = GetXMLToken( XML_COUNT );        break;
172         case SUBTOTAL_FUNC_CNT2:    sFuncStr = GetXMLToken( XML_COUNTNUMS );    break;
173         case SUBTOTAL_FUNC_MAX:     sFuncStr = GetXMLToken( XML_MAX );          break;
174         case SUBTOTAL_FUNC_MIN:     sFuncStr = GetXMLToken( XML_MIN );          break;
175         case SUBTOTAL_FUNC_NONE:    sFuncStr = GetXMLToken( XML_NONE );         break;
176         case SUBTOTAL_FUNC_PROD:    sFuncStr = GetXMLToken( XML_PRODUCT );      break;
177         case SUBTOTAL_FUNC_STD:     sFuncStr = GetXMLToken( XML_STDEV );        break;
178         case SUBTOTAL_FUNC_STDP:    sFuncStr = GetXMLToken( XML_STDEVP );       break;
179         case SUBTOTAL_FUNC_SUM:     sFuncStr = GetXMLToken( XML_SUM );          break;
180         case SUBTOTAL_FUNC_SELECTION_COUNT:                                     break;
181         // it is not needed as it is only a UI value and not document content
182 
183         case SUBTOTAL_FUNC_VAR:     sFuncStr = GetXMLToken( XML_VAR );          break;
184         case SUBTOTAL_FUNC_VARP:    sFuncStr = GetXMLToken( XML_VARP );         break;
185     }
186     ScRangeStringConverter::AssignString( rString, sFuncStr, false );
187 }
188 
GetOrientationFromString(std::u16string_view rString)189 sheet::DataPilotFieldOrientation ScXMLConverter::GetOrientationFromString(
190     std::u16string_view rString )
191 {
192     if( IsXMLToken(rString, XML_COLUMN ) )
193         return sheet::DataPilotFieldOrientation_COLUMN;
194     if( IsXMLToken(rString, XML_ROW ) )
195         return sheet::DataPilotFieldOrientation_ROW;
196     if( IsXMLToken(rString, XML_PAGE ) )
197         return sheet::DataPilotFieldOrientation_PAGE;
198     if( IsXMLToken(rString, XML_DATA ) )
199         return sheet::DataPilotFieldOrientation_DATA;
200     return sheet::DataPilotFieldOrientation_HIDDEN;
201 }
202 
GetStringFromOrientation(OUString & rString,const sheet::DataPilotFieldOrientation eOrientation)203 void ScXMLConverter::GetStringFromOrientation(
204     OUString& rString,
205     const sheet::DataPilotFieldOrientation eOrientation )
206 {
207     OUString sOrientStr;
208     switch( eOrientation )
209     {
210         case sheet::DataPilotFieldOrientation_HIDDEN:
211             sOrientStr = GetXMLToken( XML_HIDDEN );
212         break;
213         case sheet::DataPilotFieldOrientation_COLUMN:
214             sOrientStr = GetXMLToken( XML_COLUMN );
215         break;
216         case sheet::DataPilotFieldOrientation_ROW:
217             sOrientStr = GetXMLToken( XML_ROW );
218         break;
219         case sheet::DataPilotFieldOrientation_PAGE:
220             sOrientStr = GetXMLToken( XML_PAGE );
221         break;
222         case sheet::DataPilotFieldOrientation_DATA:
223             sOrientStr = GetXMLToken( XML_DATA );
224         break;
225         default:
226         {
227             // added to avoid warnings
228         }
229     }
230     ScRangeStringConverter::AssignString( rString, sOrientStr, false );
231 }
232 
GetDetObjTypeFromString(std::u16string_view rString)233 ScDetectiveObjType ScXMLConverter::GetDetObjTypeFromString( std::u16string_view rString )
234 {
235     if( IsXMLToken(rString, XML_FROM_SAME_TABLE ) )
236         return SC_DETOBJ_ARROW;
237     if( IsXMLToken(rString, XML_FROM_ANOTHER_TABLE ) )
238         return SC_DETOBJ_FROMOTHERTAB;
239     if( IsXMLToken(rString, XML_TO_ANOTHER_TABLE ) )
240         return SC_DETOBJ_TOOTHERTAB;
241     return SC_DETOBJ_NONE;
242 }
243 
GetDetOpTypeFromString(ScDetOpType & rDetOpType,std::u16string_view rString)244 bool ScXMLConverter::GetDetOpTypeFromString( ScDetOpType& rDetOpType, std::u16string_view rString )
245 {
246     if( IsXMLToken(rString, XML_TRACE_DEPENDENTS ) )
247         rDetOpType = SCDETOP_ADDSUCC;
248     else if( IsXMLToken(rString, XML_TRACE_PRECEDENTS ) )
249         rDetOpType = SCDETOP_ADDPRED;
250     else if( IsXMLToken(rString, XML_TRACE_ERRORS ) )
251         rDetOpType = SCDETOP_ADDERROR;
252     else if( IsXMLToken(rString, XML_REMOVE_DEPENDENTS ) )
253         rDetOpType = SCDETOP_DELSUCC;
254     else if( IsXMLToken(rString, XML_REMOVE_PRECEDENTS ) )
255         rDetOpType = SCDETOP_DELPRED;
256     else
257         return false;
258     return true;
259 }
260 
GetStringFromDetObjType(OUString & rString,const ScDetectiveObjType eObjType)261 void ScXMLConverter::GetStringFromDetObjType(
262         OUString& rString,
263         const ScDetectiveObjType eObjType )
264 {
265     OUString sTypeStr;
266     switch( eObjType )
267     {
268         case SC_DETOBJ_ARROW:
269             sTypeStr = GetXMLToken( XML_FROM_SAME_TABLE );
270         break;
271         case SC_DETOBJ_FROMOTHERTAB:
272             sTypeStr = GetXMLToken( XML_FROM_ANOTHER_TABLE );
273         break;
274         case SC_DETOBJ_TOOTHERTAB:
275             sTypeStr = GetXMLToken( XML_TO_ANOTHER_TABLE );
276         break;
277         default:
278         {
279             // added to avoid warnings
280         }
281     }
282     ScRangeStringConverter::AssignString( rString, sTypeStr, false );
283 }
284 
GetStringFromDetOpType(OUString & rString,const ScDetOpType eOpType)285 void ScXMLConverter::GetStringFromDetOpType(
286         OUString& rString,
287         const ScDetOpType eOpType )
288 {
289     OUString sTypeStr;
290     switch( eOpType )
291     {
292         case SCDETOP_ADDSUCC:
293             sTypeStr = GetXMLToken( XML_TRACE_DEPENDENTS );
294         break;
295         case SCDETOP_ADDPRED:
296             sTypeStr = GetXMLToken( XML_TRACE_PRECEDENTS );
297         break;
298         case SCDETOP_ADDERROR:
299             sTypeStr = GetXMLToken( XML_TRACE_ERRORS );
300         break;
301         case SCDETOP_DELSUCC:
302             sTypeStr = GetXMLToken( XML_REMOVE_DEPENDENTS );
303         break;
304         case SCDETOP_DELPRED:
305             sTypeStr = GetXMLToken( XML_REMOVE_PRECEDENTS );
306         break;
307     }
308     ScRangeStringConverter::AssignString( rString, sTypeStr, false );
309 }
310 
ConvertCellRangeAddress(OUString & sFormula)311 void ScXMLConverter::ConvertCellRangeAddress(OUString& sFormula)
312 {
313     OUStringBuffer sBuffer(sFormula.getLength());
314     bool bInQuotationMarks(false);
315     sal_Unicode chPrevious('=');
316     const sal_Unicode* p = sFormula.getStr();
317     const sal_Unicode* const pStop = p + sFormula.getLength();
318     for ( ; p < pStop; ++p)
319     {
320         const sal_Unicode c = *p;
321         if (c == '\'')
322             bInQuotationMarks = !bInQuotationMarks;
323         if (bInQuotationMarks)
324             sBuffer.append(c);
325         else if ((c != '.') ||
326                 !((chPrevious == ':') || (chPrevious == ' ') || (chPrevious == '=')))
327             sBuffer.append(c);
328         chPrevious = c;
329     }
330 
331     sFormula = sBuffer.makeStringAndClear();
332 }
333 
ConvertDateTimeToString(const DateTime & aDateTime,OUStringBuffer & sDate)334 void ScXMLConverter::ConvertDateTimeToString(const DateTime& aDateTime, OUStringBuffer& sDate)
335 {
336     css::util::DateTime aAPIDateTime = aDateTime.GetUNODateTime();
337     ::sax::Converter::convertDateTime(sDate, aAPIDateTime, nullptr);
338 }
339 
340 namespace {
341 
342 /** Enumerates different types of condition tokens. */
343 enum ScXMLConditionTokenType
344 {
345     XML_COND_TYPE_KEYWORD,          /// Simple keyword without parentheses, e.g. 'and'.
346     XML_COND_TYPE_COMPARISON,       /// Comparison rule, e.g. 'cell-content()<=2'.
347     XML_COND_TYPE_FUNCTION0,        /// Function without parameters, e.g. 'cell-content-is-whole-number()'.
348     XML_COND_TYPE_FUNCTION1,        /// Function with 1 parameter, e.g. 'is-true-formula(1+1=2)'.
349     XML_COND_TYPE_FUNCTION2         /// Function with 2 parameters, e.g. 'cell-content-is-between(1,2)'.
350 };
351 
352 struct ScXMLConditionInfo
353 {
354     ScXMLConditionToken       meToken;
355     ScXMLConditionTokenType   meType;
356     sheet::ValidationType     meValidation;
357     sheet::ConditionOperator  meOperator;
358     const char*               mpcIdentifier;
359     sal_Int32                 mnIdentLength;
360 };
361 
362 const ScXMLConditionInfo spConditionInfos[] =
363 {
364     { XML_COND_AND,                     XML_COND_TYPE_KEYWORD,    sheet::ValidationType_ANY,      sheet::ConditionOperator_NONE,        RTL_CONSTASCII_STRINGPARAM( "and" ) },
365     { XML_COND_CELLCONTENT,             XML_COND_TYPE_COMPARISON, sheet::ValidationType_ANY,      sheet::ConditionOperator_NONE,        RTL_CONSTASCII_STRINGPARAM( "cell-content" ) },
366     { XML_COND_ISBETWEEN,               XML_COND_TYPE_FUNCTION2,  sheet::ValidationType_ANY,      sheet::ConditionOperator_BETWEEN,     RTL_CONSTASCII_STRINGPARAM( "cell-content-is-between" ) },
367     { XML_COND_ISNOTBETWEEN,            XML_COND_TYPE_FUNCTION2,  sheet::ValidationType_ANY,      sheet::ConditionOperator_NOT_BETWEEN, RTL_CONSTASCII_STRINGPARAM( "cell-content-is-not-between" ) },
368     { XML_COND_ISWHOLENUMBER,           XML_COND_TYPE_FUNCTION0,  sheet::ValidationType_WHOLE,    sheet::ConditionOperator_NONE,        RTL_CONSTASCII_STRINGPARAM( "cell-content-is-whole-number" ) },
369     { XML_COND_ISDECIMALNUMBER,         XML_COND_TYPE_FUNCTION0,  sheet::ValidationType_DECIMAL,  sheet::ConditionOperator_NONE,        RTL_CONSTASCII_STRINGPARAM( "cell-content-is-decimal-number" ) },
370     { XML_COND_ISDATE,                  XML_COND_TYPE_FUNCTION0,  sheet::ValidationType_DATE,     sheet::ConditionOperator_NONE,        RTL_CONSTASCII_STRINGPARAM( "cell-content-is-date" ) },
371     { XML_COND_ISTIME,                  XML_COND_TYPE_FUNCTION0,  sheet::ValidationType_TIME,     sheet::ConditionOperator_NONE,        RTL_CONSTASCII_STRINGPARAM( "cell-content-is-time" ) },
372     { XML_COND_ISINLIST,                XML_COND_TYPE_FUNCTION1,  sheet::ValidationType_LIST,     sheet::ConditionOperator_EQUAL,       RTL_CONSTASCII_STRINGPARAM( "cell-content-is-in-list" ) },
373     { XML_COND_TEXTLENGTH,              XML_COND_TYPE_COMPARISON, sheet::ValidationType_TEXT_LEN, sheet::ConditionOperator_NONE,        RTL_CONSTASCII_STRINGPARAM( "cell-content-text-length" ) },
374     { XML_COND_TEXTLENGTH_ISBETWEEN,    XML_COND_TYPE_FUNCTION2,  sheet::ValidationType_TEXT_LEN, sheet::ConditionOperator_BETWEEN,     RTL_CONSTASCII_STRINGPARAM( "cell-content-text-length-is-between" ) },
375     { XML_COND_TEXTLENGTH_ISNOTBETWEEN, XML_COND_TYPE_FUNCTION2,  sheet::ValidationType_TEXT_LEN, sheet::ConditionOperator_NOT_BETWEEN, RTL_CONSTASCII_STRINGPARAM( "cell-content-text-length-is-not-between" ) },
376     { XML_COND_ISTRUEFORMULA,           XML_COND_TYPE_FUNCTION1,  sheet::ValidationType_CUSTOM,   sheet::ConditionOperator_FORMULA,     RTL_CONSTASCII_STRINGPARAM( "is-true-formula" ) }
377 };
378 
lclSkipWhitespace(const sal_Unicode * & rpcString,const sal_Unicode * pcEnd)379 void lclSkipWhitespace( const sal_Unicode*& rpcString, const sal_Unicode* pcEnd )
380 {
381     while( (rpcString < pcEnd) && (*rpcString <= ' ') ) ++rpcString;
382 }
383 
lclGetConditionInfo(const sal_Unicode * & rpcString,const sal_Unicode * pcEnd)384 const ScXMLConditionInfo* lclGetConditionInfo( const sal_Unicode*& rpcString, const sal_Unicode* pcEnd )
385 {
386     lclSkipWhitespace( rpcString, pcEnd );
387     /*  Search the end of an identifier name; assuming that valid identifiers
388         consist of [a-z-] only. */
389     const sal_Unicode* pcIdStart = rpcString;
390     while( (rpcString < pcEnd) && (((*rpcString >= 'a') && (*rpcString <= 'z')) || (*rpcString == '-')) ) ++rpcString;
391     sal_Int32 nLength = static_cast< sal_Int32 >( rpcString - pcIdStart );
392 
393     // search the table for an entry
394     if( nLength > 0 )
395         for(auto const &rInfo : spConditionInfos)
396             if((nLength == rInfo.mnIdentLength)
397                && (::rtl_ustr_ascii_shortenedCompare_WithLength(pcIdStart, nLength, rInfo.mpcIdentifier, nLength) == 0) )
398                 return &rInfo;
399 
400     return nullptr;
401 }
402 
lclGetConditionOperator(const sal_Unicode * & rpcString,const sal_Unicode * pcEnd)403 sheet::ConditionOperator lclGetConditionOperator( const sal_Unicode*& rpcString, const sal_Unicode* pcEnd )
404 {
405     // check for double-char operators
406     if( (rpcString + 1 < pcEnd) && (rpcString[ 1 ] == '=') )
407     {
408         sheet::ConditionOperator eOperator = sheet::ConditionOperator_NONE;
409         switch( *rpcString )
410         {
411             case '!':   eOperator = sheet::ConditionOperator_NOT_EQUAL;     break;
412             case '<':   eOperator = sheet::ConditionOperator_LESS_EQUAL;    break;
413             case '>':   eOperator = sheet::ConditionOperator_GREATER_EQUAL; break;
414         }
415         if( eOperator != sheet::ConditionOperator_NONE )
416         {
417             rpcString += 2;
418             return eOperator;
419         }
420     }
421 
422     // check for single-char operators
423     if( rpcString < pcEnd )
424     {
425         sheet::ConditionOperator eOperator = sheet::ConditionOperator_NONE;
426         switch( *rpcString )
427         {
428             case '=':   eOperator = sheet::ConditionOperator_EQUAL;     break;
429             case '<':   eOperator = sheet::ConditionOperator_LESS;      break;
430             case '>':   eOperator = sheet::ConditionOperator_GREATER;   break;
431         }
432         if( eOperator != sheet::ConditionOperator_NONE )
433         {
434             ++rpcString;
435             return eOperator;
436         }
437     }
438 
439     return sheet::ConditionOperator_NONE;
440 }
441 
442 /** Skips a literal string in a formula expression.
443 
444     @param rpcString
445         (in-out) On call, must point to the first character of the string
446         following the leading string delimiter character. On return, points to
447         the trailing string delimiter character if existing, otherwise to
448         pcEnd.
449 
450     @param pcEnd
451         The end of the string to parse.
452 
453     @param cQuoteChar
454         The string delimiter character enclosing the string.
455   */
lclSkipExpressionString(const sal_Unicode * & rpcString,const sal_Unicode * pcEnd,sal_Unicode cQuoteChar)456 void lclSkipExpressionString( const sal_Unicode*& rpcString, const sal_Unicode* pcEnd, sal_Unicode cQuoteChar )
457 {
458     if( rpcString < pcEnd )
459     {
460         sal_Int32 nLength = static_cast< sal_Int32 >( pcEnd - rpcString );
461         sal_Int32 nNextQuote = ::rtl_ustr_indexOfChar_WithLength( rpcString, nLength, cQuoteChar );
462         if( nNextQuote >= 0 )
463             rpcString += nNextQuote;
464         else
465             rpcString = pcEnd;
466     }
467 }
468 
469 /** Skips a formula expression. Processes embedded parentheses, braces, and
470     literal strings.
471 
472     @param rpcString
473         (in-out) On call, must point to the first character of the expression.
474         On return, points to the passed end character if existing, otherwise to
475         pcEnd.
476 
477     @param pcEnd
478         The end of the string to parse.
479 
480     @param cEndChar
481         The termination character following the expression.
482   */
lclSkipExpression(const sal_Unicode * & rpcString,const sal_Unicode * pcEnd,sal_Unicode cEndChar)483 void lclSkipExpression( const sal_Unicode*& rpcString, const sal_Unicode* pcEnd, sal_Unicode cEndChar )
484 {
485     while( rpcString < pcEnd )
486     {
487         if( *rpcString == cEndChar )
488             return;
489         switch( *rpcString )
490         {
491             case '(':       lclSkipExpression( ++rpcString, pcEnd, ')' );           break;
492             case '{':       lclSkipExpression( ++rpcString, pcEnd, '}' );           break;
493             case '"':       lclSkipExpressionString( ++rpcString, pcEnd, '"' );     break;
494             case '\'':      lclSkipExpressionString( ++rpcString, pcEnd, '\'' );    break;
495         }
496         if( rpcString < pcEnd ) ++rpcString;
497     }
498 }
499 
500 /** Extracts a formula expression. Processes embedded parentheses, braces, and
501     literal strings.
502 
503     @param rpcString
504         (in-out) On call, must point to the first character of the expression.
505         On return, points *behind* the passed end character if existing,
506         otherwise to pcEnd.
507 
508     @param pcEnd
509         The end of the string to parse.
510 
511     @param cEndChar
512         The termination character following the expression.
513   */
514 /** Tries to skip an empty pair of parentheses (which may contain whitespace
515     characters).
516 
517     @return
518         True on success, rpcString points behind the closing parentheses then.
519  */
lclSkipEmptyParentheses(const sal_Unicode * & rpcString,const sal_Unicode * pcEnd)520 bool lclSkipEmptyParentheses( const sal_Unicode*& rpcString, const sal_Unicode* pcEnd )
521 {
522     if( (rpcString < pcEnd) && (*rpcString == '(') )
523     {
524         lclSkipWhitespace( ++rpcString, pcEnd );
525         if( (rpcString < pcEnd) && (*rpcString == ')') )
526         {
527             ++rpcString;
528             return true;
529         }
530     }
531     return false;
532 }
533 
534 } // namespace
535 
parseCondition(ScXMLConditionParseResult & rParseResult,const OUString & rAttribute,sal_Int32 nStartIndex)536 void ScXMLConditionHelper::parseCondition(
537         ScXMLConditionParseResult& rParseResult, const OUString& rAttribute, sal_Int32 nStartIndex )
538 {
539     rParseResult.meToken = XML_COND_INVALID;
540     if( (nStartIndex < 0) || (nStartIndex >= rAttribute.getLength()) ) return;
541 
542     // try to find an identifier
543     const sal_Unicode* pcBegin = rAttribute.getStr();
544     const sal_Unicode* pcString = pcBegin + nStartIndex;
545     const sal_Unicode* pcEnd = pcBegin + rAttribute.getLength();
546     const ScXMLConditionInfo* pCondInfo = lclGetConditionInfo( pcString, pcEnd );
547     if( !pCondInfo )
548         return;
549 
550     // insert default values into parse result (may be changed below)
551     rParseResult.meValidation = pCondInfo->meValidation;
552     rParseResult.meOperator = pCondInfo->meOperator;
553     // continue parsing dependent on token type
554     switch( pCondInfo->meType )
555     {
556         case XML_COND_TYPE_KEYWORD:
557             // nothing specific has to follow, success
558             rParseResult.meToken = pCondInfo->meToken;
559         break;
560 
561         case XML_COND_TYPE_COMPARISON:
562             // format is <condition>()<operator><expression>
563             if( lclSkipEmptyParentheses( pcString, pcEnd ) )
564             {
565                 rParseResult.meOperator = lclGetConditionOperator( pcString, pcEnd );
566                 if( rParseResult.meOperator != sheet::ConditionOperator_NONE )
567                 {
568                     lclSkipWhitespace( pcString, pcEnd );
569                     if( pcString < pcEnd )
570                     {
571                         rParseResult.meToken = pCondInfo->meToken;
572                         // comparison must be at end of attribute, remaining text is the formula
573                         rParseResult.maOperand1 = OUString( pcString, static_cast< sal_Int32 >( pcEnd - pcString ) );
574                     }
575                 }
576             }
577         break;
578 
579         case XML_COND_TYPE_FUNCTION0:
580             // format is <condition>()
581             if( lclSkipEmptyParentheses( pcString, pcEnd ) )
582                 rParseResult.meToken = pCondInfo->meToken;
583         break;
584 
585         case XML_COND_TYPE_FUNCTION1:
586             // format is <condition>(<expression>)
587             if( (pcString < pcEnd) && (*pcString == '(') )
588             {
589                 rParseResult.maOperand1 = getExpression( ++pcString, pcEnd, ')' );
590                 if( !rParseResult.maOperand1.isEmpty() )
591                     rParseResult.meToken = pCondInfo->meToken;
592             }
593         break;
594 
595         case XML_COND_TYPE_FUNCTION2:
596             // format is <condition>(<expression1>,<expression2>)
597             if( (pcString < pcEnd) && (*pcString == '(') )
598             {
599                 rParseResult.maOperand1 = getExpression( ++pcString, pcEnd, ',' );
600                 if( !rParseResult.maOperand1.isEmpty() )
601                 {
602                     rParseResult.maOperand2 = getExpression( pcString, pcEnd, ')' );
603                     if( !rParseResult.maOperand2.isEmpty() )
604                         rParseResult.meToken = pCondInfo->meToken;
605                 }
606             }
607         break;
608     }
609     rParseResult.mnEndIndex = static_cast< sal_Int32 >( pcString - pcBegin );
610 }
611 
getExpression(const sal_Unicode * & rpcString,const sal_Unicode * pcEnd,sal_Unicode cEndChar)612 OUString ScXMLConditionHelper::getExpression( const sal_Unicode*& rpcString, const sal_Unicode* pcEnd, sal_Unicode cEndChar )
613 {
614     OUString aExp;
615     const sal_Unicode* pcExpStart = rpcString;
616     lclSkipExpression( rpcString, pcEnd, cEndChar );
617     if( rpcString < pcEnd )
618     {
619         aExp = OUString( pcExpStart, static_cast< sal_Int32 >( rpcString - pcExpStart ) ).trim();
620         ++rpcString;
621     }
622     return aExp;
623 }
624 
625 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
626