Version 6.4.0.3, tag libreoffice-6.4.0.3
[LibreOffice.git] / sc / source / filter / xml / XMLConverter.cxx
blob303795ace556355df57d003769ffc11bac60a126
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::getUnoTunnelImplementation<ScModelObj>( xModel );
39 return pDocObj ? pDocObj->GetDocument() : nullptr;
41 return nullptr;
44 sheet::GeneralFunction ScXMLConverter::GetFunctionFromString( const OUString& 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( const OUString& 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( const OUString& 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 void ScXMLConverter::GetStringFromFunction(
134 OUString& rString,
135 sal_Int16 eFunction )
137 OUString sFuncStr;
138 switch( eFunction )
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:
156 assert(false);
159 ScRangeStringConverter::AssignString( rString, sFuncStr, false );
162 void ScXMLConverter::GetStringFromFunction(
163 OUString& rString,
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 ScRangeStringConverter::AssignString( rString, sFuncStr, false );
189 sheet::DataPilotFieldOrientation ScXMLConverter::GetOrientationFromString(
190 const OUString& rString )
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;
203 void ScXMLConverter::GetStringFromOrientation(
204 OUString& rString,
205 const sheet::DataPilotFieldOrientation eOrientation )
207 OUString sOrientStr;
208 switch( eOrientation )
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:
227 // added to avoid warnings
230 ScRangeStringConverter::AssignString( rString, sOrientStr, false );
233 ScDetectiveObjType ScXMLConverter::GetDetObjTypeFromString( const OUString& rString )
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;
244 bool ScXMLConverter::GetDetOpTypeFromString( ScDetOpType& rDetOpType, const OUString& rString )
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;
261 void ScXMLConverter::GetStringFromDetObjType(
262 OUString& rString,
263 const ScDetectiveObjType eObjType )
265 OUString sTypeStr;
266 switch( eObjType )
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:
279 // added to avoid warnings
282 ScRangeStringConverter::AssignString( rString, sTypeStr, false );
285 void ScXMLConverter::GetStringFromDetOpType(
286 OUString& rString,
287 const ScDetOpType eOpType )
289 OUString sTypeStr;
290 switch( eOpType )
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;
308 ScRangeStringConverter::AssignString( rString, sTypeStr, false );
311 void ScXMLConverter::ConvertCellRangeAddress(OUString& sFormula)
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)
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;
331 sFormula = sBuffer.makeStringAndClear();
334 void ScXMLConverter::ConvertDateTimeToString(const DateTime& aDateTime, OUStringBuffer& sDate)
336 css::util::DateTime aAPIDateTime = aDateTime.GetUNODateTime();
337 ::sax::Converter::convertDateTime(sDate, aAPIDateTime, nullptr);
340 namespace {
342 /** Enumerates different types of condition tokens. */
343 enum ScXMLConditionTokenType
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)'.
352 struct ScXMLConditionInfo
354 ScXMLConditionToken const meToken;
355 ScXMLConditionTokenType const meType;
356 sheet::ValidationType const meValidation;
357 sheet::ConditionOperator const meOperator;
358 const sal_Char* mpcIdentifier;
359 sal_Int32 const mnIdentLength;
362 static const ScXMLConditionInfo spConditionInfos[] =
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" ) }
379 void lclSkipWhitespace( const sal_Unicode*& rpcString, const sal_Unicode* pcEnd )
381 while( (rpcString < pcEnd) && (*rpcString <= ' ') ) ++rpcString;
384 const ScXMLConditionInfo* lclGetConditionInfo( const sal_Unicode*& rpcString, const sal_Unicode* pcEnd )
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 );
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;
400 return nullptr;
403 sheet::ConditionOperator lclGetConditionOperator( const sal_Unicode*& rpcString, const sal_Unicode* pcEnd )
405 // check for double-char operators
406 if( (rpcString + 1 < pcEnd) && (rpcString[ 1 ] == '=') )
408 sheet::ConditionOperator eOperator = sheet::ConditionOperator_NONE;
409 switch( *rpcString )
411 case '!': eOperator = sheet::ConditionOperator_NOT_EQUAL; break;
412 case '<': eOperator = sheet::ConditionOperator_LESS_EQUAL; break;
413 case '>': eOperator = sheet::ConditionOperator_GREATER_EQUAL; break;
415 if( eOperator != sheet::ConditionOperator_NONE )
417 rpcString += 2;
418 return eOperator;
422 // check for single-char operators
423 if( rpcString < pcEnd )
425 sheet::ConditionOperator eOperator = sheet::ConditionOperator_NONE;
426 switch( *rpcString )
428 case '=': eOperator = sheet::ConditionOperator_EQUAL; break;
429 case '<': eOperator = sheet::ConditionOperator_LESS; break;
430 case '>': eOperator = sheet::ConditionOperator_GREATER; break;
432 if( eOperator != sheet::ConditionOperator_NONE )
434 ++rpcString;
435 return eOperator;
439 return sheet::ConditionOperator_NONE;
442 /** Skips a literal string in a formula expression.
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.
450 @param pcEnd
451 The end of the string to parse.
453 @param cQuoteChar
454 The string delimiter character enclosing the string.
456 void lclSkipExpressionString( const sal_Unicode*& rpcString, const sal_Unicode* pcEnd, sal_Unicode cQuoteChar )
458 if( rpcString < pcEnd )
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;
469 /** Skips a formula expression. Processes embedded parentheses, braces, and
470 literal strings.
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.
477 @param pcEnd
478 The end of the string to parse.
480 @param cEndChar
481 The termination character following the expression.
483 void lclSkipExpression( const sal_Unicode*& rpcString, const sal_Unicode* pcEnd, sal_Unicode cEndChar )
485 while( rpcString < pcEnd )
487 if( *rpcString == cEndChar )
488 return;
489 switch( *rpcString )
491 case '(': lclSkipExpression( ++rpcString, pcEnd, ')' ); break;
492 case '{': lclSkipExpression( ++rpcString, pcEnd, '}' ); break;
493 case '"': lclSkipExpressionString( ++rpcString, pcEnd, '"' ); break;
494 case '\'': lclSkipExpressionString( ++rpcString, pcEnd, '\'' ); break;
496 if( rpcString < pcEnd ) ++rpcString;
500 /** Extracts a formula expression. Processes embedded parentheses, braces, and
501 literal strings.
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.
508 @param pcEnd
509 The end of the string to parse.
511 @param cEndChar
512 The termination character following the expression.
514 /** Tries to skip an empty pair of parentheses (which may contain whitespace
515 characters).
517 @return
518 True on success, rpcString points behind the closing parentheses then.
520 bool lclSkipEmptyParentheses( const sal_Unicode*& rpcString, const sal_Unicode* pcEnd )
522 if( (rpcString < pcEnd) && (*rpcString == '(') )
524 lclSkipWhitespace( ++rpcString, pcEnd );
525 if( (rpcString < pcEnd) && (*rpcString == ')') )
527 ++rpcString;
528 return true;
531 return false;
534 } // namespace
536 void ScXMLConditionHelper::parseCondition(
537 ScXMLConditionParseResult& rParseResult, const OUString& rAttribute, sal_Int32 nStartIndex )
539 rParseResult.meToken = XML_COND_INVALID;
540 if( (nStartIndex < 0) || (nStartIndex >= rAttribute.getLength()) ) return;
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 if( const ScXMLConditionInfo* pCondInfo = lclGetConditionInfo( pcString, pcEnd ) )
548 // insert default values into parse result (may be changed below)
549 rParseResult.meValidation = pCondInfo->meValidation;
550 rParseResult.meOperator = pCondInfo->meOperator;
551 // continue parsing dependent on token type
552 switch( pCondInfo->meType )
554 case XML_COND_TYPE_KEYWORD:
555 // nothing specific has to follow, success
556 rParseResult.meToken = pCondInfo->meToken;
557 break;
559 case XML_COND_TYPE_COMPARISON:
560 // format is <condition>()<operator><expression>
561 if( lclSkipEmptyParentheses( pcString, pcEnd ) )
563 rParseResult.meOperator = lclGetConditionOperator( pcString, pcEnd );
564 if( rParseResult.meOperator != sheet::ConditionOperator_NONE )
566 lclSkipWhitespace( pcString, pcEnd );
567 if( pcString < pcEnd )
569 rParseResult.meToken = pCondInfo->meToken;
570 // comparison must be at end of attribute, remaining text is the formula
571 rParseResult.maOperand1 = OUString( pcString, static_cast< sal_Int32 >( pcEnd - pcString ) );
575 break;
577 case XML_COND_TYPE_FUNCTION0:
578 // format is <condition>()
579 if( lclSkipEmptyParentheses( pcString, pcEnd ) )
580 rParseResult.meToken = pCondInfo->meToken;
581 break;
583 case XML_COND_TYPE_FUNCTION1:
584 // format is <condition>(<expression>)
585 if( (pcString < pcEnd) && (*pcString == '(') )
587 rParseResult.maOperand1 = getExpression( ++pcString, pcEnd, ')' );
588 if( !rParseResult.maOperand1.isEmpty() )
589 rParseResult.meToken = pCondInfo->meToken;
591 break;
593 case XML_COND_TYPE_FUNCTION2:
594 // format is <condition>(<expression1>,<expression2>)
595 if( (pcString < pcEnd) && (*pcString == '(') )
597 rParseResult.maOperand1 = getExpression( ++pcString, pcEnd, ',' );
598 if( !rParseResult.maOperand1.isEmpty() )
600 rParseResult.maOperand2 = getExpression( pcString, pcEnd, ')' );
601 if( !rParseResult.maOperand2.isEmpty() )
602 rParseResult.meToken = pCondInfo->meToken;
605 break;
607 rParseResult.mnEndIndex = static_cast< sal_Int32 >( pcString - pcBegin );
611 OUString ScXMLConditionHelper::getExpression( const sal_Unicode*& rpcString, const sal_Unicode* pcEnd, sal_Unicode cEndChar )
613 OUString aExp;
614 const sal_Unicode* pcExpStart = rpcString;
615 lclSkipExpression( rpcString, pcEnd, cEndChar );
616 if( rpcString < pcEnd )
618 aExp = OUString( pcExpStart, static_cast< sal_Int32 >( rpcString - pcExpStart ) ).trim();
619 ++rpcString;
621 return aExp;
624 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */