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