nss: upgrade to release 3.73
[LibreOffice.git] / forms / source / xforms / xpathlib / xpathlib.cxx
blob68cb43a6cdbaff4a751b06b4fefd65855a6a9bdc
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 .
21 #include <string.h>
22 #include <sal/types.h>
23 #include <rtl/ustring.hxx>
24 #include <rtl/string.hxx>
25 #include <rtl/strbuf.hxx>
26 #include <tools/date.hxx>
27 #include <tools/time.hxx>
28 #include <tools/datetime.hxx>
30 #include <com/sun/star/uno/Reference.hxx>
31 #include <com/sun/star/uno/Sequence.hxx>
32 #include <com/sun/star/xforms/XModel.hpp>
33 #include <com/sun/star/xml/dom/XNode.hpp>
34 #include <com/sun/star/xml/dom/XDocument.hpp>
35 #include <com/sun/star/lang/XUnoTunnel.hpp>
37 #include <boost/lexical_cast.hpp>
38 #include <libxml/xpathInternals.h>
40 #include "xpathlib.hxx"
41 #include "extension.hxx"
43 // C interface
45 using namespace com::sun::star::uno;
46 using namespace com::sun::star::xml::dom;
47 using namespace com::sun::star::xforms;
48 using namespace com::sun::star::lang;
50 xmlXPathFunction xforms_lookupFunc(void *, const xmlChar *xname, const xmlChar *)
53 const char *name = reinterpret_cast<char const *>(xname);
54 if (strcmp("boolean-from-string", name)==0)
55 return xforms_booleanFromStringFunction;
56 else if ((strcmp("if", name))==0)
57 return xforms_ifFunction;
58 else if ((strcmp("avg", name))==0)
59 return xforms_avgFunction;
60 else if ((strcmp("min", name))==0)
61 return xforms_minFunction;
62 else if ((strcmp("max", name))==0)
63 return xforms_maxFunction;
64 else if ((strcmp("count-non-empty", name))==0)
65 return xforms_countNonEmptyFunction;
66 else if ((strcmp("index", name))==0)
67 return xforms_indexFunction;
68 else if ((strcmp("property", name))==0)
69 return xforms_propertyFunction;
70 else if ((strcmp("now", name))==0)
71 return xforms_nowFunction;
72 else if ((strcmp("days-from-date", name))==0)
73 return xforms_daysFromDateFunction;
74 else if ((strcmp("seconds-from-dateTime", name))==0)
75 return xforms_secondsFromDateTimeFunction;
76 else if ((strcmp("seconds", name))==0)
77 return xforms_secondsFunction;
78 else if ((strcmp("months", name))==0)
79 return xforms_monthsFunction;
80 else if ((strcmp("instance", name))==0)
81 return xforms_instanceFunction;
82 else if ((strcmp("current", name))==0)
83 return xforms_currentFunction;
84 else
85 return nullptr;
88 // boolean functions
89 void xforms_booleanFromStringFunction(xmlXPathParserContextPtr ctxt, int nargs)
91 if (nargs != 1) XP_ERROR(XPATH_INVALID_ARITY);
92 xmlChar *pString = xmlXPathPopString(ctxt);
93 if (xmlXPathCheckError(ctxt)) XP_ERROR(XPATH_INVALID_TYPE);
94 OUString aString(reinterpret_cast<char*>(pString), strlen(reinterpret_cast<char*>(pString)), RTL_TEXTENCODING_UTF8);
95 if (aString.equalsIgnoreAsciiCase("true") ||
96 aString.equalsIgnoreAsciiCase("1"))
97 xmlXPathReturnTrue(ctxt);
98 else if (aString.equalsIgnoreAsciiCase("false") ||
99 aString.equalsIgnoreAsciiCase("0"))
100 xmlXPathReturnFalse(ctxt);
101 else
102 XP_ERROR(XPATH_NUMBER_ERROR);
105 void xforms_ifFunction(xmlXPathParserContextPtr ctxt, int nargs)
107 if (nargs != 3) XP_ERROR(XPATH_INVALID_ARITY);
108 xmlChar *s2 = xmlXPathPopString(ctxt);
110 if (xmlXPathCheckError(ctxt)) XP_ERROR(XPATH_INVALID_TYPE);
111 xmlChar *s1 = xmlXPathPopString(ctxt);
112 if (xmlXPathCheckError(ctxt)) XP_ERROR(XPATH_INVALID_TYPE);
113 bool aBool = xmlXPathPopBoolean(ctxt);
114 if (xmlXPathCheckError(ctxt)) XP_ERROR(XPATH_INVALID_TYPE);
116 if (aBool)
117 xmlXPathReturnString(ctxt, s1);
118 else
119 xmlXPathReturnString(ctxt, s2);
123 // Number Functions
124 void xforms_avgFunction(xmlXPathParserContextPtr ctxt, int nargs)
126 // use sum(), div() and count()
127 if (nargs != 1) XP_ERROR(XPATH_INVALID_ARITY);
129 // save nodeset
130 xmlXPathObjectPtr pObject = valuePop(ctxt);
131 if (xmlXPathCheckError(ctxt)) XP_ERROR(XPATH_INVALID_TYPE);
132 //push back a copy
133 valuePush(ctxt, xmlXPathObjectCopy(pObject));
134 // get the Sum
135 xmlXPathSumFunction(ctxt, 1);
136 double nSum = xmlXPathPopNumber(ctxt);
137 // push a copy once more
138 valuePush(ctxt, xmlXPathObjectCopy(pObject));
139 xmlXPathCountFunction(ctxt, 1);
140 double nCount = xmlXPathPopNumber(ctxt);
141 // push args for div()
142 xmlXPathReturnNumber(ctxt, nSum);
143 xmlXPathReturnNumber(ctxt, nCount);
144 xmlXPathDivValues(ctxt);
145 // the result is now on the ctxt stack
146 xmlXPathFreeObject(pObject);
149 void xforms_minFunction(xmlXPathParserContextPtr ctxt, int nargs)
151 if (nargs != 1) XP_ERROR(XPATH_INVALID_ARITY);
152 xmlNodeSetPtr pNodeSet = xmlXPathPopNodeSet(ctxt);
153 if (xmlXPathCheckError(ctxt)) XP_ERROR(XPATH_INVALID_TYPE);
154 double nMinimum = 0;
155 for (int i = 0; i < xmlXPathNodeSetGetLength(pNodeSet); i++)
157 double nNumber = xmlXPathCastNodeToNumber(xmlXPathNodeSetItem(pNodeSet, i));
158 if (xmlXPathIsNaN(nNumber))
160 xmlXPathReturnNumber(ctxt, xmlXPathNAN);
161 return;
163 if (i == 0)
164 nMinimum = nNumber;
165 else if (nNumber < nMinimum)
166 nMinimum = nNumber;
168 xmlXPathReturnNumber(ctxt, nMinimum);
171 void xforms_maxFunction(xmlXPathParserContextPtr ctxt, int nargs)
173 if (nargs != 1) XP_ERROR(XPATH_INVALID_ARITY);
174 xmlNodeSetPtr pNodeSet = xmlXPathPopNodeSet(ctxt);
175 if (xmlXPathCheckError(ctxt)) XP_ERROR(XPATH_INVALID_TYPE);
176 double nMaximum = 0;
177 for (int i = 0; i < xmlXPathNodeSetGetLength(pNodeSet); i++)
179 double nNumber = xmlXPathCastNodeToNumber(xmlXPathNodeSetItem(pNodeSet, i));
180 if (xmlXPathIsNaN(nNumber))
182 xmlXPathReturnNumber(ctxt, xmlXPathNAN);
183 return;
185 if (i == 0)
186 nMaximum = nNumber;
187 else if (nNumber > nMaximum)
188 nMaximum = nNumber;
190 xmlXPathReturnNumber(ctxt, nMaximum);
192 void xforms_countNonEmptyFunction(xmlXPathParserContextPtr ctxt, int nargs)
194 if (nargs != 1) XP_ERROR(XPATH_INVALID_ARITY);
195 xmlNodeSetPtr pNodeSet = xmlXPathPopNodeSet(ctxt);
196 if (xmlXPathCheckError(ctxt)) XP_ERROR(XPATH_INVALID_TYPE);
197 sal_Int32 nNotEmpty = 0;
198 for (int i = 0; i < xmlXPathNodeSetGetLength(pNodeSet); i++)
200 const xmlChar *aString = xmlXPathCastNodeToString(xmlXPathNodeSetItem(pNodeSet, i));
201 if (*aString != 0) nNotEmpty++;
203 xmlXPathReturnNumber(ctxt, nNotEmpty);
205 void xforms_indexFunction(xmlXPathParserContextPtr /*ctxt*/, int /*nargs*/)
207 // function index takes a string argument that is the IDREF of a
208 // 'repeat' and returns the current 1-based position of the repeat
209 // index of the identified repeat -- see xforms/9.3.1
211 // doc.getElementByID
212 // (...)
215 // String Functions
216 void xforms_propertyFunction(xmlXPathParserContextPtr ctxt, int nargs)
218 if (nargs != 1) XP_ERROR(XPATH_INVALID_ARITY);
219 xmlChar* pString = xmlXPathPopString(ctxt);
220 if (xmlXPathCheckError(ctxt)) XP_ERROR(XPATH_INVALID_TYPE);
221 OUString aString(reinterpret_cast<char*>(pString), strlen(reinterpret_cast<char*>(pString)), RTL_TEXTENCODING_UTF8);
222 if (aString.equalsIgnoreAsciiCase("version"))
223 xmlXPathReturnString(ctxt, reinterpret_cast<xmlChar *>(const_cast<char *>("1.0")));
224 else if (aString.equalsIgnoreAsciiCase("conformance-level"))
225 xmlXPathReturnString(ctxt, reinterpret_cast<xmlChar *>(const_cast<char *>("conformance")));
226 else
227 xmlXPathReturnEmptyString(ctxt);
230 // Date and Time Functions
232 static OString makeDateTimeString (const DateTime& aDateTime)
234 OStringBuffer aDateTimeString;
235 aDateTimeString.append(static_cast<sal_Int32>(aDateTime.GetYear()));
236 aDateTimeString.append('-');
237 if (aDateTime.GetMonth()<10) aDateTimeString.append('0');
238 aDateTimeString.append(static_cast<sal_Int32>(aDateTime.GetMonth()));
239 aDateTimeString.append('-');
240 if (aDateTime.GetDay()<10) aDateTimeString.append('0');
241 aDateTimeString.append(static_cast<sal_Int32>(aDateTime.GetDay()));
242 aDateTimeString.append('T');
243 if (aDateTime.GetHour()<10) aDateTimeString.append('0');
244 aDateTimeString.append(static_cast<sal_Int32>(aDateTime.GetHour()));
245 aDateTimeString.append(':');
246 if (aDateTime.GetMin()<10) aDateTimeString.append('0');
247 aDateTimeString.append(static_cast<sal_Int32>(aDateTime.GetMin()));
248 aDateTimeString.append(':');
249 if (aDateTime.GetSec()<10) aDateTimeString.append('0');
250 aDateTimeString.append(static_cast<sal_Int32>(aDateTime.GetSec()));
251 aDateTimeString.append('Z');
253 return aDateTimeString.makeStringAndClear();
256 // returns current system date and time in canonical xsd:dateTime
257 // format
258 void xforms_nowFunction(xmlXPathParserContextPtr ctxt, int /*nargs*/)
261 A single lexical representation, which is a subset of the lexical representations
262 allowed by [ISO 8601], is allowed for dateTime. This lexical representation is the
263 [ISO 8601] extended format CCYY-MM-DDThh:mm:ss where "CC" represents the century,
264 "YY" the year, "MM" the month and "DD" the day, preceded by an optional leading "-"
265 sign to indicate a negative number. If the sign is omitted, "+" is assumed. The letter
266 "T" is the date/time separator and "hh", "mm", "ss" represent hour, minute and second
267 respectively.
271 3.2.7.2 Canonical representation
272 The canonical representation for dateTime is defined by prohibiting certain options
273 from the Lexical representation (par.3.2.7.1). Specifically, either the time zone must
274 be omitted or, if present, the time zone must be Coordinated Universal tools::Time (UTC)
275 indicated by a "Z".
277 DateTime aDateTime( DateTime::SYSTEM );
278 OString aDateTimeString = makeDateTimeString(aDateTime);
279 xmlChar *pString = static_cast<xmlChar*>(xmlMalloc(aDateTimeString.getLength()+1));
280 strncpy(reinterpret_cast<char*>(pString), aDateTimeString.getStr(), aDateTimeString.getLength());
281 pString[aDateTimeString.getLength()] = 0;
282 xmlXPathReturnString(ctxt, pString);
285 static bool parseDateTime(const OUString& aString, DateTime& aDateTime)
287 // take apart a canonical literal xsd:dateTime string
288 //CCYY-MM-DDThh:mm:ss(Z)
290 OUString aDateTimeString = aString.trim();
292 // check length
293 if (aDateTimeString.getLength() < 19 || aDateTimeString.getLength() > 20)
294 return false;
296 sal_Int32 nIndex = 0;
297 sal_Int32 nYear = aDateTimeString.getToken(0, '-', nIndex).toInt32();
298 sal_Int32 nMonth = aDateTimeString.getToken(0, '-', nIndex).toInt32();
299 sal_Int32 nDay = aDateTimeString.getToken(0, 'T', nIndex).toInt32();
300 sal_Int32 nHour = aDateTimeString.getToken(0, ':', nIndex).toInt32();
301 sal_Int32 nMinute = aDateTimeString.getToken(0, ':', nIndex).toInt32();
302 sal_Int32 nSecond = aDateTimeString.getToken(0, 'Z', nIndex).toInt32();
304 Date tmpDate(static_cast<sal_uInt16>(nDay), static_cast<sal_uInt16>(nMonth), static_cast<sal_uInt16>(nYear));
305 tools::Time tmpTime(nHour, nMinute, nSecond);
306 DateTime tmpDateTime(tmpDate, tmpTime);
307 if (aString.lastIndexOf('Z') < 0)
308 tmpDateTime.ConvertToUTC();
310 aDateTime = tmpDateTime;
312 return true;
316 void xforms_daysFromDateFunction(xmlXPathParserContextPtr ctxt, int nargs)
318 // number of days from 1970-01-01 to supplied xsd:date(Time)
320 if (nargs != 1) XP_ERROR(XPATH_INVALID_ARITY);
321 xmlChar* pString = xmlXPathPopString(ctxt);
322 if (xmlXPathCheckError(ctxt)) XP_ERROR(XPATH_INVALID_TYPE);
323 OUString aString(reinterpret_cast<char*>(pString), strlen(reinterpret_cast<char*>(pString)), RTL_TEXTENCODING_UTF8);
325 DateTime aDateTime( DateTime::EMPTY );
326 if (parseDateTime(aString, aDateTime))
328 Date aReferenceDate(1, 1, 1970);
329 sal_Int32 nDays = aDateTime - aReferenceDate;
330 xmlXPathReturnNumber(ctxt, nDays);
332 else
333 xmlXPathReturnNumber(ctxt, xmlXPathNAN);
339 void xforms_secondsFromDateTimeFunction(xmlXPathParserContextPtr ctxt, int nargs)
341 // number of seconds from 1970-01-01T00:00:00Z to supplied xsd:date(Time)
343 if (nargs != 1) XP_ERROR(XPATH_INVALID_ARITY);
344 xmlChar* pString = xmlXPathPopString(ctxt);
345 if (xmlXPathCheckError(ctxt)) XP_ERROR(XPATH_INVALID_TYPE);
346 OUString aString(reinterpret_cast<char*>(pString), strlen(reinterpret_cast<char*>(pString)), RTL_TEXTENCODING_UTF8);
348 DateTime aDateTime( DateTime::EMPTY );
350 if (parseDateTime(aString, aDateTime))
352 Date aReferenceDate(1, 1, 1970);
353 sal_Int32 nDays = aDateTime - aReferenceDate;
354 sal_Int32 nSeconds = nDays * 24 * 60 * 60;
355 nSeconds += aDateTime.GetHour() * 60 * 60;
356 nSeconds += aDateTime.GetMin() * 60;
357 nSeconds += aDateTime.GetSec();
358 xmlXPathReturnNumber(ctxt, nSeconds);
360 else
361 xmlXPathReturnNumber(ctxt, xmlXPathNAN);
365 static bool parseDuration(const xmlChar* aString, bool& bNegative, sal_Int32& nYears, sal_Int32& nMonth, sal_Int32& nDays,
366 sal_Int32& nHours, sal_Int32& nMinutes, sal_Int32& nSeconds)
368 bool bTime = false; // in part after T
369 const xmlChar *pString = aString;
371 if (pString[0] == '-') {
372 bNegative = true;
373 pString++;
376 if (pString[0] != 'P')
378 return false;
381 pString++;
382 const xmlChar* pToken = pString;
383 while(pToken[0] != 0)
385 switch(pToken[0]) {
386 case 'Y':
387 nYears = boost::lexical_cast<sal_Int32>(pString, pString-pToken);
388 pString = ++pToken;
389 break;
390 case 'M':
391 if (!bTime)
392 nMonth = boost::lexical_cast<sal_Int32>(pString, pString-pToken);
393 else
394 nMinutes = boost::lexical_cast<sal_Int32>(pString, pString-pToken);
395 pString = ++pToken;
396 break;
397 case 'D':
398 nDays = boost::lexical_cast<sal_Int32>(pString, pString-pToken);
399 pString = ++pToken;
400 break;
401 case 'H':
402 nHours = boost::lexical_cast<sal_Int32>(pString, pString-pToken);
403 pString = ++pToken;
404 break;
405 case 'S':
406 nSeconds = boost::lexical_cast<sal_Int32>(pString, pString-pToken);
407 pString = ++pToken;
408 break;
409 case 'T':
410 bTime = true;
411 pString = ++pToken;
412 break;
413 default:
414 pToken++;
417 return true;
420 void xforms_secondsFunction(xmlXPathParserContextPtr ctxt, int nargs)
422 // convert a xsd:duration to seconds
423 // (-)PnYnMnDTnHnMnS
424 if (nargs != 1) XP_ERROR(XPATH_INVALID_ARITY);
425 xmlChar* pString = xmlXPathPopString(ctxt);
426 if (xmlXPathCheckError(ctxt)) XP_ERROR(XPATH_INVALID_TYPE);
428 bool bNegative = false;
429 sal_Int32 nYears = 0;
430 sal_Int32 nMonths = 0;
431 sal_Int32 nDays = 0;
432 sal_Int32 nHours = 0;
433 sal_Int32 nMinutes = 0;
434 sal_Int32 nSeconds = 0;
436 if (parseDuration(pString, bNegative, nYears, nMonths, nDays, nHours, nMinutes, nSeconds))
438 nSeconds += nMinutes*60;
439 nSeconds += nHours*60*60;
440 nSeconds += nDays*24*60*60;
441 // year and month are ignored according to spec
442 if (bNegative)
443 nSeconds = 0 - nSeconds;
444 xmlXPathReturnNumber(ctxt, nSeconds);
446 else
447 xmlXPathReturnNumber(ctxt, xmlXPathNAN);
450 void xforms_monthsFunction(xmlXPathParserContextPtr ctxt, int nargs)
452 // convert a xsd:duration to seconds
453 // (-)PnYnMnDTnHnMnS
454 if (nargs != 1) XP_ERROR(XPATH_INVALID_ARITY);
455 xmlChar* pString = xmlXPathPopString(ctxt);
456 if (xmlXPathCheckError(ctxt)) XP_ERROR(XPATH_INVALID_TYPE);
458 bool bNegative = false;
459 sal_Int32 nYears = 0;
460 sal_Int32 nMonths = 0;
461 sal_Int32 nDays = 0;
462 sal_Int32 nHours = 0;
463 sal_Int32 nMinutes = 0;
464 sal_Int32 nSeconds = 0;
466 if (parseDuration(pString, bNegative, nYears, nMonths, nDays, nHours, nMinutes, nSeconds))
468 nMonths += nYears*12;
469 // Days, Hours, Minutes and seconds are ignored, see spec
470 if (bNegative)
471 nMonths = 0 - nMonths;
472 xmlXPathReturnNumber(ctxt, nMonths);
474 else
475 xmlXPathReturnNumber(ctxt, xmlXPathNAN);
479 // Node-set Functions
480 void xforms_instanceFunction(xmlXPathParserContextPtr ctxt, int nargs)
482 if (nargs != 1) XP_ERROR(XPATH_INVALID_ARITY);
483 xmlChar *pString = xmlXPathPopString(ctxt);
484 if (xmlXPathCheckError(ctxt)) XP_ERROR(XPATH_INVALID_TYPE);
485 OUString aString(reinterpret_cast<char*>(pString), strlen(reinterpret_cast<char*>(pString)), RTL_TEXTENCODING_UTF8);
487 Reference< XModel > aModel = static_cast<CLibxml2XFormsExtension*>(ctxt->context->funcLookupData)->getModel();
488 if (aModel.is())
490 Reference< XDocument > aInstance = aModel->getInstanceDocument(aString);
491 if (aInstance.is())
493 try {
494 // xmlXPathObjectPtr xmlXPathNewNodeSet (xmlNodePtr val);
495 Reference< XUnoTunnel > aTunnel(aInstance, UNO_QUERY_THROW);
496 xmlNodePtr pNode = reinterpret_cast< xmlNodePtr >( aTunnel->getSomething(Sequence< sal_Int8 >()) );
497 xmlXPathObjectPtr pObject = xmlXPathNewNodeSet(pNode);
498 xmlXPathReturnNodeSet(ctxt, pObject->nodesetval);
499 } catch (const RuntimeException&)
501 xmlXPathReturnEmptyNodeSet(ctxt);
504 else
505 xmlXPathReturnEmptyNodeSet(ctxt);
507 else
508 xmlXPathReturnEmptyNodeSet(ctxt);
512 // Node-set Functions, XForms 1.1
513 void xforms_currentFunction(xmlXPathParserContextPtr ctxt, int nargs)
515 if (nargs != 0) XP_ERROR(XPATH_INVALID_ARITY);
517 Reference< XNode > aNode = static_cast<CLibxml2XFormsExtension*>(ctxt->context->funcLookupData)->getContextNode();
519 if (aNode.is())
521 try {
522 Reference< XUnoTunnel > aTunnel(aNode, UNO_QUERY_THROW);
523 xmlNodePtr pNode = reinterpret_cast< xmlNodePtr >( aTunnel->getSomething(Sequence< sal_Int8 >()) );
524 xmlXPathObjectPtr pObject = xmlXPathNewNodeSet(pNode);
525 xmlXPathReturnNodeSet(ctxt, pObject->nodesetval);
527 catch (const RuntimeException&)
529 xmlXPathReturnEmptyNodeSet(ctxt);
532 else
533 xmlXPathReturnEmptyNodeSet(ctxt);
536 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */