cid#1607171 Data race condition
[LibreOffice.git] / sc / source / filter / xml / XMLConverter.cxx
blobfd7dcb472847916b2afa2322a8e21ad9ac32fd12
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
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>
31 using namespace ::com::sun::star;
32 using namespace xmloff::token;
34 ScDocument* ScXMLConverter::GetScDocument( const uno::Reference< frame::XModel >& xModel )
36 if (xModel.is())
38 ScModelObj* pDocObj = comphelper::getFromUnoTunnel<ScModelObj>( xModel );
39 return pDocObj ? pDocObj->GetDocument() : nullptr;
41 return nullptr;
44 sheet::GeneralFunction ScXMLConverter::GetFunctionFromString( std::u16string_view sFunction )
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;
73 ScGeneralFunction ScXMLConverter::GetFunctionFromString2( std::u16string_view sFunction )
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;
104 ScSubTotalFunc ScXMLConverter::GetSubTotalFuncFromString( std::u16string_view sFunction )
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;
133 OUString ScXMLConverter::GetStringFromFunction(
134 sal_Int16 eFunction )
136 OUString sFuncStr;
137 switch( eFunction )
139 case sheet::GeneralFunction2::AUTO: sFuncStr = GetXMLToken( XML_AUTO ); break;
140 case sheet::GeneralFunction2::AVERAGE: sFuncStr = GetXMLToken( XML_AVERAGE ); break;
141 case sheet::GeneralFunction2::MEDIAN: sFuncStr = GetXMLToken( XML_MEDIAN ); break;
142 case sheet::GeneralFunction2::COUNT: sFuncStr = GetXMLToken( XML_COUNT ); break;
143 case sheet::GeneralFunction2::COUNTNUMS: sFuncStr = GetXMLToken( XML_COUNTNUMS ); break;
144 case sheet::GeneralFunction2::MAX: sFuncStr = GetXMLToken( XML_MAX ); break;
145 case sheet::GeneralFunction2::MIN: sFuncStr = GetXMLToken( XML_MIN ); break;
146 case sheet::GeneralFunction2::NONE: sFuncStr = GetXMLToken( XML_NONE ); break;
147 case sheet::GeneralFunction2::PRODUCT: sFuncStr = GetXMLToken( XML_PRODUCT ); break;
148 case sheet::GeneralFunction2::STDEV: sFuncStr = GetXMLToken( XML_STDEV ); break;
149 case sheet::GeneralFunction2::STDEVP: sFuncStr = GetXMLToken( XML_STDEVP ); break;
150 case sheet::GeneralFunction2::SUM: sFuncStr = GetXMLToken( XML_SUM ); break;
151 case sheet::GeneralFunction2::VAR: sFuncStr = GetXMLToken( XML_VAR ); break;
152 case sheet::GeneralFunction2::VARP: sFuncStr = GetXMLToken( XML_VARP ); break;
153 default:
155 assert(false);
158 OUString str;
159 ScRangeStringConverter::AssignString( str, sFuncStr, false );
160 return str;
163 OUString ScXMLConverter::GetStringFromFunction(
164 const ScSubTotalFunc eFunction )
166 OUString sFuncStr;
167 switch( eFunction )
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
183 case SUBTOTAL_FUNC_VAR: sFuncStr = GetXMLToken( XML_VAR ); break;
184 case SUBTOTAL_FUNC_VARP: sFuncStr = GetXMLToken( XML_VARP ); break;
186 OUString str;
187 ScRangeStringConverter::AssignString( str, sFuncStr, false );
188 return str;
191 sheet::DataPilotFieldOrientation ScXMLConverter::GetOrientationFromString(
192 std::u16string_view rString )
194 if( IsXMLToken(rString, XML_COLUMN ) )
195 return sheet::DataPilotFieldOrientation_COLUMN;
196 if( IsXMLToken(rString, XML_ROW ) )
197 return sheet::DataPilotFieldOrientation_ROW;
198 if( IsXMLToken(rString, XML_PAGE ) )
199 return sheet::DataPilotFieldOrientation_PAGE;
200 if( IsXMLToken(rString, XML_DATA ) )
201 return sheet::DataPilotFieldOrientation_DATA;
202 return sheet::DataPilotFieldOrientation_HIDDEN;
205 OUString ScXMLConverter::GetStringFromOrientation(
206 const sheet::DataPilotFieldOrientation eOrientation )
208 OUString sOrientStr;
209 switch( eOrientation )
211 case sheet::DataPilotFieldOrientation_HIDDEN:
212 sOrientStr = GetXMLToken( XML_HIDDEN );
213 break;
214 case sheet::DataPilotFieldOrientation_COLUMN:
215 sOrientStr = GetXMLToken( XML_COLUMN );
216 break;
217 case sheet::DataPilotFieldOrientation_ROW:
218 sOrientStr = GetXMLToken( XML_ROW );
219 break;
220 case sheet::DataPilotFieldOrientation_PAGE:
221 sOrientStr = GetXMLToken( XML_PAGE );
222 break;
223 case sheet::DataPilotFieldOrientation_DATA:
224 sOrientStr = GetXMLToken( XML_DATA );
225 break;
226 default:
228 // added to avoid warnings
231 OUString str;
232 ScRangeStringConverter::AssignString( str, sOrientStr, false );
233 return str;
236 ScDetectiveObjType ScXMLConverter::GetDetObjTypeFromString( std::u16string_view rString )
238 if( IsXMLToken(rString, XML_FROM_SAME_TABLE ) )
239 return SC_DETOBJ_ARROW;
240 if( IsXMLToken(rString, XML_FROM_ANOTHER_TABLE ) )
241 return SC_DETOBJ_FROMOTHERTAB;
242 if( IsXMLToken(rString, XML_TO_ANOTHER_TABLE ) )
243 return SC_DETOBJ_TOOTHERTAB;
244 return SC_DETOBJ_NONE;
247 bool ScXMLConverter::GetDetOpTypeFromString( ScDetOpType& rDetOpType, std::u16string_view rString )
249 if( IsXMLToken(rString, XML_TRACE_DEPENDENTS ) )
250 rDetOpType = SCDETOP_ADDSUCC;
251 else if( IsXMLToken(rString, XML_TRACE_PRECEDENTS ) )
252 rDetOpType = SCDETOP_ADDPRED;
253 else if( IsXMLToken(rString, XML_TRACE_ERRORS ) )
254 rDetOpType = SCDETOP_ADDERROR;
255 else if( IsXMLToken(rString, XML_REMOVE_DEPENDENTS ) )
256 rDetOpType = SCDETOP_DELSUCC;
257 else if( IsXMLToken(rString, XML_REMOVE_PRECEDENTS ) )
258 rDetOpType = SCDETOP_DELPRED;
259 else
260 return false;
261 return true;
264 OUString ScXMLConverter::GetStringFromDetObjType(
265 const ScDetectiveObjType eObjType )
267 OUString sTypeStr;
268 switch( eObjType )
270 case SC_DETOBJ_ARROW:
271 sTypeStr = GetXMLToken( XML_FROM_SAME_TABLE );
272 break;
273 case SC_DETOBJ_FROMOTHERTAB:
274 sTypeStr = GetXMLToken( XML_FROM_ANOTHER_TABLE );
275 break;
276 case SC_DETOBJ_TOOTHERTAB:
277 sTypeStr = GetXMLToken( XML_TO_ANOTHER_TABLE );
278 break;
279 default:
281 // added to avoid warnings
284 OUString str;
285 ScRangeStringConverter::AssignString( str, sTypeStr, false );
286 return str;
289 OUString ScXMLConverter::GetStringFromDetOpType(
290 const ScDetOpType eOpType )
292 OUString sTypeStr;
293 switch( eOpType )
295 case SCDETOP_ADDSUCC:
296 sTypeStr = GetXMLToken( XML_TRACE_DEPENDENTS );
297 break;
298 case SCDETOP_ADDPRED:
299 sTypeStr = GetXMLToken( XML_TRACE_PRECEDENTS );
300 break;
301 case SCDETOP_ADDERROR:
302 sTypeStr = GetXMLToken( XML_TRACE_ERRORS );
303 break;
304 case SCDETOP_DELSUCC:
305 sTypeStr = GetXMLToken( XML_REMOVE_DEPENDENTS );
306 break;
307 case SCDETOP_DELPRED:
308 sTypeStr = GetXMLToken( XML_REMOVE_PRECEDENTS );
309 break;
311 OUString str;
312 ScRangeStringConverter::AssignString( str, sTypeStr, false );
313 return str;
316 void ScXMLConverter::ConvertCellRangeAddress(OUString& sFormula)
318 OUStringBuffer sBuffer(sFormula.getLength());
319 bool bInQuotationMarks(false);
320 sal_Unicode chPrevious('=');
321 const sal_Unicode* p = sFormula.getStr();
322 const sal_Unicode* const pStop = p + sFormula.getLength();
323 for ( ; p < pStop; ++p)
325 const sal_Unicode c = *p;
326 if (c == '\'')
327 bInQuotationMarks = !bInQuotationMarks;
328 if (bInQuotationMarks)
329 sBuffer.append(c);
330 else if ((c != '.') ||
331 !((chPrevious == ':') || (chPrevious == ' ') || (chPrevious == '=')))
332 sBuffer.append(c);
333 chPrevious = c;
336 sFormula = sBuffer.makeStringAndClear();
339 void ScXMLConverter::ConvertDateTimeToString(const DateTime& aDateTime, OUStringBuffer& sDate)
341 css::util::DateTime aAPIDateTime = aDateTime.GetUNODateTime();
342 ::sax::Converter::convertDateTime(sDate, aAPIDateTime, nullptr);
345 namespace {
347 /** Enumerates different types of condition tokens. */
348 enum ScXMLConditionTokenType
350 XML_COND_TYPE_KEYWORD, /// Simple keyword without parentheses, e.g. 'and'.
351 XML_COND_TYPE_COMPARISON, /// Comparison rule, e.g. 'cell-content()<=2'.
352 XML_COND_TYPE_FUNCTION0, /// Function without parameters, e.g. 'cell-content-is-whole-number()'.
353 XML_COND_TYPE_FUNCTION1, /// Function with 1 parameter, e.g. 'is-true-formula(1+1=2)'.
354 XML_COND_TYPE_FUNCTION2 /// Function with 2 parameters, e.g. 'cell-content-is-between(1,2)'.
357 struct ScXMLConditionInfo
359 ScXMLConditionToken meToken;
360 ScXMLConditionTokenType meType;
361 sheet::ValidationType meValidation;
362 sheet::ConditionOperator meOperator;
363 const char* mpcIdentifier;
364 sal_Int32 mnIdentLength;
367 const ScXMLConditionInfo spConditionInfos[] =
369 { XML_COND_AND, XML_COND_TYPE_KEYWORD, sheet::ValidationType_ANY, sheet::ConditionOperator_NONE, RTL_CONSTASCII_STRINGPARAM( "and" ) },
370 { XML_COND_CELLCONTENT, XML_COND_TYPE_COMPARISON, sheet::ValidationType_ANY, sheet::ConditionOperator_NONE, RTL_CONSTASCII_STRINGPARAM( "cell-content" ) },
371 { XML_COND_ISBETWEEN, XML_COND_TYPE_FUNCTION2, sheet::ValidationType_ANY, sheet::ConditionOperator_BETWEEN, RTL_CONSTASCII_STRINGPARAM( "cell-content-is-between" ) },
372 { XML_COND_ISNOTBETWEEN, XML_COND_TYPE_FUNCTION2, sheet::ValidationType_ANY, sheet::ConditionOperator_NOT_BETWEEN, RTL_CONSTASCII_STRINGPARAM( "cell-content-is-not-between" ) },
373 { XML_COND_ISWHOLENUMBER, XML_COND_TYPE_FUNCTION0, sheet::ValidationType_WHOLE, sheet::ConditionOperator_NONE, RTL_CONSTASCII_STRINGPARAM( "cell-content-is-whole-number" ) },
374 { XML_COND_ISDECIMALNUMBER, XML_COND_TYPE_FUNCTION0, sheet::ValidationType_DECIMAL, sheet::ConditionOperator_NONE, RTL_CONSTASCII_STRINGPARAM( "cell-content-is-decimal-number" ) },
375 { XML_COND_ISDATE, XML_COND_TYPE_FUNCTION0, sheet::ValidationType_DATE, sheet::ConditionOperator_NONE, RTL_CONSTASCII_STRINGPARAM( "cell-content-is-date" ) },
376 { XML_COND_ISTIME, XML_COND_TYPE_FUNCTION0, sheet::ValidationType_TIME, sheet::ConditionOperator_NONE, RTL_CONSTASCII_STRINGPARAM( "cell-content-is-time" ) },
377 { XML_COND_ISINLIST, XML_COND_TYPE_FUNCTION1, sheet::ValidationType_LIST, sheet::ConditionOperator_EQUAL, RTL_CONSTASCII_STRINGPARAM( "cell-content-is-in-list" ) },
378 { XML_COND_TEXTLENGTH, XML_COND_TYPE_COMPARISON, sheet::ValidationType_TEXT_LEN, sheet::ConditionOperator_NONE, RTL_CONSTASCII_STRINGPARAM( "cell-content-text-length" ) },
379 { XML_COND_TEXTLENGTH_ISBETWEEN, XML_COND_TYPE_FUNCTION2, sheet::ValidationType_TEXT_LEN, sheet::ConditionOperator_BETWEEN, RTL_CONSTASCII_STRINGPARAM( "cell-content-text-length-is-between" ) },
380 { 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" ) },
381 { XML_COND_ISTRUEFORMULA, XML_COND_TYPE_FUNCTION1, sheet::ValidationType_CUSTOM, sheet::ConditionOperator_FORMULA, RTL_CONSTASCII_STRINGPARAM( "is-true-formula" ) }
384 void lclSkipWhitespace( const sal_Unicode*& rpcString, const sal_Unicode* pcEnd )
386 while( (rpcString < pcEnd) && (*rpcString <= ' ') ) ++rpcString;
389 const ScXMLConditionInfo* lclGetConditionInfo( const sal_Unicode*& rpcString, const sal_Unicode* pcEnd )
391 lclSkipWhitespace( rpcString, pcEnd );
392 /* Search the end of an identifier name; assuming that valid identifiers
393 consist of [a-z-] only. */
394 const sal_Unicode* pcIdStart = rpcString;
395 while( (rpcString < pcEnd) && (((*rpcString >= 'a') && (*rpcString <= 'z')) || (*rpcString == '-')) ) ++rpcString;
396 sal_Int32 nLength = static_cast< sal_Int32 >( rpcString - pcIdStart );
398 // search the table for an entry
399 if( nLength > 0 )
400 for(auto const &rInfo : spConditionInfos)
401 if((nLength == rInfo.mnIdentLength)
402 && (::rtl_ustr_ascii_shortenedCompare_WithLength(pcIdStart, nLength, rInfo.mpcIdentifier, nLength) == 0) )
403 return &rInfo;
405 return nullptr;
408 sheet::ConditionOperator lclGetConditionOperator( const sal_Unicode*& rpcString, const sal_Unicode* pcEnd )
410 // check for double-char operators
411 if( (rpcString + 1 < pcEnd) && (rpcString[ 1 ] == '=') )
413 sheet::ConditionOperator eOperator = sheet::ConditionOperator_NONE;
414 switch( *rpcString )
416 case '!': eOperator = sheet::ConditionOperator_NOT_EQUAL; break;
417 case '<': eOperator = sheet::ConditionOperator_LESS_EQUAL; break;
418 case '>': eOperator = sheet::ConditionOperator_GREATER_EQUAL; break;
420 if( eOperator != sheet::ConditionOperator_NONE )
422 rpcString += 2;
423 return eOperator;
427 // check for single-char operators
428 if( rpcString < pcEnd )
430 sheet::ConditionOperator eOperator = sheet::ConditionOperator_NONE;
431 switch( *rpcString )
433 case '=': eOperator = sheet::ConditionOperator_EQUAL; break;
434 case '<': eOperator = sheet::ConditionOperator_LESS; break;
435 case '>': eOperator = sheet::ConditionOperator_GREATER; break;
437 if( eOperator != sheet::ConditionOperator_NONE )
439 ++rpcString;
440 return eOperator;
444 return sheet::ConditionOperator_NONE;
447 /** Skips a literal string in a formula expression.
449 @param rpcString
450 (in-out) On call, must point to the first character of the string
451 following the leading string delimiter character. On return, points to
452 the trailing string delimiter character if existing, otherwise to
453 pcEnd.
455 @param pcEnd
456 The end of the string to parse.
458 @param cQuoteChar
459 The string delimiter character enclosing the string.
461 void lclSkipExpressionString( const sal_Unicode*& rpcString, const sal_Unicode* pcEnd, sal_Unicode cQuoteChar )
463 if( rpcString < pcEnd )
465 sal_Int32 nLength = static_cast< sal_Int32 >( pcEnd - rpcString );
466 sal_Int32 nNextQuote = ::rtl_ustr_indexOfChar_WithLength( rpcString, nLength, cQuoteChar );
467 if( nNextQuote >= 0 )
468 rpcString += nNextQuote;
469 else
470 rpcString = pcEnd;
474 /** Skips a formula expression. Processes embedded parentheses, braces, and
475 literal strings.
477 @param rpcString
478 (in-out) On call, must point to the first character of the expression.
479 On return, points to the passed end character if existing, otherwise to
480 pcEnd.
482 @param pcEnd
483 The end of the string to parse.
485 @param cEndChar
486 The termination character following the expression.
488 void lclSkipExpression( const sal_Unicode*& rpcString, const sal_Unicode* pcEnd, sal_Unicode cEndChar )
490 while( rpcString < pcEnd )
492 if( *rpcString == cEndChar )
493 return;
494 switch( *rpcString )
496 case '(': lclSkipExpression( ++rpcString, pcEnd, ')' ); break;
497 case '{': lclSkipExpression( ++rpcString, pcEnd, '}' ); break;
498 case '"': lclSkipExpressionString( ++rpcString, pcEnd, '"' ); break;
499 case '\'': lclSkipExpressionString( ++rpcString, pcEnd, '\'' ); break;
501 if( rpcString < pcEnd ) ++rpcString;
505 /** Extracts a formula expression. Processes embedded parentheses, braces, and
506 literal strings.
508 @param rpcString
509 (in-out) On call, must point to the first character of the expression.
510 On return, points *behind* the passed end character if existing,
511 otherwise to pcEnd.
513 @param pcEnd
514 The end of the string to parse.
516 @param cEndChar
517 The termination character following the expression.
519 /** Tries to skip an empty pair of parentheses (which may contain whitespace
520 characters).
522 @return
523 True on success, rpcString points behind the closing parentheses then.
525 bool lclSkipEmptyParentheses( const sal_Unicode*& rpcString, const sal_Unicode* pcEnd )
527 if( (rpcString < pcEnd) && (*rpcString == '(') )
529 lclSkipWhitespace( ++rpcString, pcEnd );
530 if( (rpcString < pcEnd) && (*rpcString == ')') )
532 ++rpcString;
533 return true;
536 return false;
539 } // namespace
541 void ScXMLConditionHelper::parseCondition(
542 ScXMLConditionParseResult& rParseResult, const OUString& rAttribute, sal_Int32 nStartIndex )
544 rParseResult.meToken = XML_COND_INVALID;
545 if( (nStartIndex < 0) || (nStartIndex >= rAttribute.getLength()) ) return;
547 // try to find an identifier
548 const sal_Unicode* pcBegin = rAttribute.getStr();
549 const sal_Unicode* pcString = pcBegin + nStartIndex;
550 const sal_Unicode* pcEnd = pcBegin + rAttribute.getLength();
551 const ScXMLConditionInfo* pCondInfo = lclGetConditionInfo( pcString, pcEnd );
552 if( !pCondInfo )
553 return;
555 // insert default values into parse result (may be changed below)
556 rParseResult.meValidation = pCondInfo->meValidation;
557 rParseResult.meOperator = pCondInfo->meOperator;
558 // continue parsing dependent on token type
559 switch( pCondInfo->meType )
561 case XML_COND_TYPE_KEYWORD:
562 // nothing specific has to follow, success
563 rParseResult.meToken = pCondInfo->meToken;
564 break;
566 case XML_COND_TYPE_COMPARISON:
567 // format is <condition>()<operator><expression>
568 if( lclSkipEmptyParentheses( pcString, pcEnd ) )
570 rParseResult.meOperator = lclGetConditionOperator( pcString, pcEnd );
571 if( rParseResult.meOperator != sheet::ConditionOperator_NONE )
573 lclSkipWhitespace( pcString, pcEnd );
574 if( pcString < pcEnd )
576 rParseResult.meToken = pCondInfo->meToken;
577 // comparison must be at end of attribute, remaining text is the formula
578 rParseResult.maOperand1 = OUString( pcString, static_cast< sal_Int32 >( pcEnd - pcString ) );
582 break;
584 case XML_COND_TYPE_FUNCTION0:
585 // format is <condition>()
586 if( lclSkipEmptyParentheses( pcString, pcEnd ) )
587 rParseResult.meToken = pCondInfo->meToken;
588 break;
590 case XML_COND_TYPE_FUNCTION1:
591 // format is <condition>(<expression>)
592 if( (pcString < pcEnd) && (*pcString == '(') )
594 rParseResult.maOperand1 = getExpression( ++pcString, pcEnd, ')' );
595 if( !rParseResult.maOperand1.isEmpty() )
596 rParseResult.meToken = pCondInfo->meToken;
598 break;
600 case XML_COND_TYPE_FUNCTION2:
601 // format is <condition>(<expression1>,<expression2>)
602 if( (pcString < pcEnd) && (*pcString == '(') )
604 rParseResult.maOperand1 = getExpression( ++pcString, pcEnd, ',' );
605 if( !rParseResult.maOperand1.isEmpty() )
607 rParseResult.maOperand2 = getExpression( pcString, pcEnd, ')' );
608 if( !rParseResult.maOperand2.isEmpty() )
609 rParseResult.meToken = pCondInfo->meToken;
612 break;
614 rParseResult.mnEndIndex = static_cast< sal_Int32 >( pcString - pcBegin );
617 OUString ScXMLConditionHelper::getExpression( const sal_Unicode*& rpcString, const sal_Unicode* pcEnd, sal_Unicode cEndChar )
619 OUString aExp;
620 const sal_Unicode* pcExpStart = rpcString;
621 lclSkipExpression( rpcString, pcEnd, cEndChar );
622 if( rpcString < pcEnd )
624 aExp = OUString( pcExpStart, static_cast< sal_Int32 >( rpcString - pcExpStart ) ).trim();
625 ++rpcString;
627 return aExp;
630 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */