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