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