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