bump product version to 5.0.4.1
[LibreOffice.git] / forms / source / xforms / xpathlib / xpathlib.cxx
blob0f3a9fcd0df2ce190bb4e94d8652e2c509024abb
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/alloc.h>
24 #include <rtl/ustring.hxx>
25 #include <rtl/string.hxx>
26 #include <rtl/ustrbuf.hxx>
27 #include <rtl/strbuf.hxx>
28 #include <tools/date.hxx>
29 #include <tools/time.hxx>
30 #include <tools/datetime.hxx>
32 #include <com/sun/star/uno/Reference.hxx>
33 #include <com/sun/star/uno/Sequence.hxx>
34 #include <com/sun/star/uno/Any.hxx>
35 #include <com/sun/star/xforms/XModel.hpp>
36 #include <com/sun/star/xml/dom/XNode.hpp>
37 #include <com/sun/star/xml/dom/XDocument.hpp>
38 #include <com/sun/star/lang/XUnoTunnel.hpp>
40 #include "xpathlib.hxx"
42 #include "extension.hxx"
44 // C interface
46 using namespace com::sun::star::uno;
47 using namespace com::sun::star::xml::dom;
48 using namespace com::sun::star::xforms;
49 using namespace com::sun::star::lang;
51 xmlXPathFunction xforms_lookupFunc(void *, const xmlChar *xname, const xmlChar *)
54 const char *name = reinterpret_cast<char const *>(xname);
55 if (strcmp("boolean-from-string", name)==0)
56 return xforms_booleanFromStringFunction;
57 else if ((strcmp("if", name))==0)
58 return xforms_ifFunction;
59 else if ((strcmp("avg", name))==0)
60 return xforms_avgFunction;
61 else if ((strcmp("min", name))==0)
62 return xforms_minFunction;
63 else if ((strcmp("max", name))==0)
64 return xforms_maxFunction;
65 else if ((strcmp("count-non-empty", name))==0)
66 return xforms_countNonEmptyFunction;
67 else if ((strcmp("index", name))==0)
68 return xforms_indexFunction;
69 else if ((strcmp("property", name))==0)
70 return xforms_propertyFunction;
71 else if ((strcmp("now", name))==0)
72 return xforms_nowFunction;
73 else if ((strcmp("days-from-date", name))==0)
74 return xforms_daysFromDateFunction;
75 else if ((strcmp("seconds-from-dateTime", name))==0)
76 return xforms_secondsFromDateTimeFunction;
77 else if ((strcmp("seconds", name))==0)
78 return xforms_secondsFuction;
79 else if ((strcmp("months", name))==0)
80 return xforms_monthsFuction;
81 else if ((strcmp("instance", name))==0)
82 return xforms_instanceFuction;
83 else if ((strcmp("current", name))==0)
84 return xforms_currentFunction;
85 else
86 return NULL;
89 // boolean functions
90 void xforms_booleanFromStringFunction(xmlXPathParserContextPtr ctxt, int nargs)
92 if (nargs != 1) XP_ERROR(XPATH_INVALID_ARITY);
93 xmlChar *pString = xmlXPathPopString(ctxt);
94 if (xmlXPathCheckError(ctxt)) XP_ERROR(XPATH_INVALID_TYPE);
95 OUString aString(reinterpret_cast<char*>(pString), strlen(reinterpret_cast<char*>(pString)), RTL_TEXTENCODING_UTF8);
96 if (aString.equalsIgnoreAsciiCase("true") ||
97 aString.equalsIgnoreAsciiCase("1"))
98 xmlXPathReturnTrue(ctxt);
99 else if (aString.equalsIgnoreAsciiCase("false") ||
100 aString.equalsIgnoreAsciiCase("0"))
101 xmlXPathReturnFalse(ctxt);
102 else
103 XP_ERROR(XPATH_NUMBER_ERROR);
106 void xforms_ifFunction(xmlXPathParserContextPtr ctxt, int nargs)
108 if (nargs != 3) XP_ERROR(XPATH_INVALID_ARITY);
109 xmlChar *s2 = xmlXPathPopString(ctxt);
111 if (xmlXPathCheckError(ctxt)) XP_ERROR(XPATH_INVALID_TYPE);
112 xmlChar *s1 = xmlXPathPopString(ctxt);
113 if (xmlXPathCheckError(ctxt)) XP_ERROR(XPATH_INVALID_TYPE);
114 bool aBool = xmlXPathPopBoolean(ctxt);
115 if (xmlXPathCheckError(ctxt)) XP_ERROR(XPATH_INVALID_TYPE);
117 if (aBool)
118 xmlXPathReturnString(ctxt, s1);
119 else
120 xmlXPathReturnString(ctxt, s2);
124 // Number Functions
125 void xforms_avgFunction(xmlXPathParserContextPtr ctxt, int nargs)
127 // use sum(), div() and count()
128 if (nargs != 1) XP_ERROR(XPATH_INVALID_ARITY);
130 // save nodeset
131 xmlXPathObjectPtr pObject = valuePop(ctxt);
132 if (xmlXPathCheckError(ctxt)) XP_ERROR(XPATH_INVALID_TYPE);
133 //push back a copy
134 valuePush(ctxt, xmlXPathObjectCopy(pObject));
135 // get the Sum
136 xmlXPathSumFunction(ctxt, 1);
137 double nSum = xmlXPathPopNumber(ctxt);
138 // push a copy once more
139 valuePush(ctxt, xmlXPathObjectCopy(pObject));
140 xmlXPathCountFunction(ctxt, 1);
141 double nCount = xmlXPathPopNumber(ctxt);
142 // push args for div()
143 xmlXPathReturnNumber(ctxt, nSum);
144 xmlXPathReturnNumber(ctxt, nCount);
145 xmlXPathDivValues(ctxt);
146 // the result is now on the ctxt stack
147 xmlXPathFreeObject(pObject);
150 void xforms_minFunction(xmlXPathParserContextPtr ctxt, int nargs)
152 if (nargs != 1) XP_ERROR(XPATH_INVALID_ARITY);
153 xmlNodeSetPtr pNodeSet = xmlXPathPopNodeSet(ctxt);
154 if (xmlXPathCheckError(ctxt)) XP_ERROR(XPATH_INVALID_TYPE);
155 double nMinimum = 0;
156 for (int i = 0; i < xmlXPathNodeSetGetLength(pNodeSet); i++)
158 double nNumber = xmlXPathCastNodeToNumber(xmlXPathNodeSetItem(pNodeSet, i));
159 if (xmlXPathIsNaN(nNumber))
161 xmlXPathReturnNumber(ctxt, xmlXPathNAN);
162 return;
164 if (i == 0)
165 nMinimum = nNumber;
166 else if (nNumber < nMinimum)
167 nMinimum = nNumber;
169 xmlXPathReturnNumber(ctxt, nMinimum);
172 void xforms_maxFunction(xmlXPathParserContextPtr ctxt, int nargs)
174 if (nargs != 1) XP_ERROR(XPATH_INVALID_ARITY);
175 xmlNodeSetPtr pNodeSet = xmlXPathPopNodeSet(ctxt);
176 if (xmlXPathCheckError(ctxt)) XP_ERROR(XPATH_INVALID_TYPE);
177 double nMaximum = 0;
178 for (int i = 0; i < xmlXPathNodeSetGetLength(pNodeSet); i++)
180 double nNumber = xmlXPathCastNodeToNumber(xmlXPathNodeSetItem(pNodeSet, i));
181 if (xmlXPathIsNaN(nNumber))
183 xmlXPathReturnNumber(ctxt, xmlXPathNAN);
184 return;
186 if (i == 0)
187 nMaximum = nNumber;
188 else if (nNumber > nMaximum)
189 nMaximum = nNumber;
191 xmlXPathReturnNumber(ctxt, nMaximum);
193 void xforms_countNonEmptyFunction(xmlXPathParserContextPtr ctxt, int nargs)
195 if (nargs != 1) XP_ERROR(XPATH_INVALID_ARITY);
196 xmlNodeSetPtr pNodeSet = xmlXPathPopNodeSet(ctxt);
197 if (xmlXPathCheckError(ctxt)) XP_ERROR(XPATH_INVALID_TYPE);
198 sal_Int32 nNotEmpty = 0;
199 for (int i = 0; i < xmlXPathNodeSetGetLength(pNodeSet); i++)
201 const xmlChar *aString = xmlXPathCastNodeToString(xmlXPathNodeSetItem(pNodeSet, i));
202 if (*aString != 0) nNotEmpty++;
204 xmlXPathReturnNumber(ctxt, nNotEmpty);
206 void xforms_indexFunction(xmlXPathParserContextPtr /*ctxt*/, int /*nargs*/)
208 // function index takes a string argument that is the IDREF of a
209 // 'repeat' and returns the current 1-based position of the repeat
210 // index of the identified repeat -- see xforms/9.3.1
212 // doc.getElementByID
213 // (...)
216 // String Functions
217 static const char* _version = "1.0";
218 static const char* _conformance = "conformance";
219 void xforms_propertyFunction(xmlXPathParserContextPtr ctxt, int nargs)
221 if (nargs != 1) XP_ERROR(XPATH_INVALID_ARITY);
222 xmlChar* pString = xmlXPathPopString(ctxt);
223 if (xmlXPathCheckError(ctxt)) XP_ERROR(XPATH_INVALID_TYPE);
224 OUString aString(reinterpret_cast<char*>(pString), strlen(reinterpret_cast<char*>(pString)), RTL_TEXTENCODING_UTF8);
225 if (aString.equalsIgnoreAsciiCase("version"))
226 xmlXPathReturnString(ctxt, reinterpret_cast<xmlChar *>(const_cast<char *>(_version)));
227 else if (aString.equalsIgnoreAsciiCase("conformance-level"))
228 xmlXPathReturnString(ctxt, reinterpret_cast<xmlChar *>(const_cast<char *>(_conformance)));
229 else
230 xmlXPathReturnEmptyString(ctxt);
233 // Date and Time Functions
235 static OString makeDateTimeString (const DateTime& aDateTime, bool bUTC = true)
237 OStringBuffer aDateTimeString;
238 aDateTimeString.append((sal_Int32)aDateTime.GetYear());
239 aDateTimeString.append("-");
240 if (aDateTime.GetMonth()<10) aDateTimeString.append("0");
241 aDateTimeString.append((sal_Int32)aDateTime.GetMonth());
242 aDateTimeString.append("-");
243 if (aDateTime.GetDay()<10) aDateTimeString.append("0");
244 aDateTimeString.append((sal_Int32)aDateTime.GetDay());
245 aDateTimeString.append("T");
246 if (aDateTime.GetHour()<10) aDateTimeString.append("0");
247 aDateTimeString.append((sal_Int32)aDateTime.GetHour());
248 aDateTimeString.append(":");
249 if (aDateTime.GetMin()<10) aDateTimeString.append("0");
250 aDateTimeString.append((sal_Int32)aDateTime.GetMin());
251 aDateTimeString.append(":");
252 if (aDateTime.GetSec()<10) aDateTimeString.append("0");
253 aDateTimeString.append((sal_Int32)aDateTime.GetSec());
254 if (bUTC) aDateTimeString.append("Z");
256 return aDateTimeString.makeStringAndClear();
259 // returns current system date and time in canonical xsd:dateTime
260 // format
261 void xforms_nowFunction(xmlXPathParserContextPtr ctxt, int /*nargs*/)
264 A single lexical representation, which is a subset of the lexical representations
265 allowed by [ISO 8601], is allowed for dateTime. This lexical representation is the
266 [ISO 8601] extended format CCYY-MM-DDThh:mm:ss where "CC" represents the century,
267 "YY" the year, "MM" the month and "DD" the day, preceded by an optional leading "-"
268 sign to indicate a negative number. If the sign is omitted, "+" is assumed. The letter
269 "T" is the date/time separator and "hh", "mm", "ss" represent hour, minute and second
270 respectively.
274 3.2.7.2 Canonical representation
275 The canonical representation for dateTime is defined by prohibiting certain options
276 from the Lexical representation (par.3.2.7.1). Specifically, either the time zone must
277 be omitted or, if present, the time zone must be Coordinated Universal tools::Time (UTC)
278 indicated by a "Z".
280 DateTime aDateTime( DateTime::SYSTEM );
281 OString aDateTimeString = makeDateTimeString(aDateTime);
282 xmlChar *pString = static_cast<xmlChar*>(xmlMalloc(aDateTimeString.getLength()+1));
283 strncpy(reinterpret_cast<char*>(pString), aDateTimeString.getStr(), aDateTimeString.getLength());
284 pString[aDateTimeString.getLength()] = 0;
285 xmlXPathReturnString(ctxt, pString);
288 static bool parseDateTime(const OUString& aString, DateTime& aDateTime)
290 // take apart a canonical literal xsd:dateTime string
291 //CCYY-MM-DDThh:mm:ss(Z)
293 OUString aDateTimeString = aString.trim();
295 // check length
296 if (aDateTimeString.getLength() < 19 || aDateTimeString.getLength() > 20)
297 return false;
299 sal_Int32 nDateLength = 10;
300 sal_Int32 nTimeLength = 8;
302 OUString aUTCString("Z");
304 OUString aDateString = aDateTimeString.copy(0, nDateLength);
305 OUString aTimeString = aDateTimeString.copy(nDateLength+1, nTimeLength);
307 sal_Int32 nIndex = 0;
308 sal_Int32 nYear = aDateString.getToken(0, '-', nIndex).toInt32();
309 sal_Int32 nMonth = aDateString.getToken(0, '-', nIndex).toInt32();
310 sal_Int32 nDay = aDateString.getToken(0, '-', nIndex).toInt32();
311 nIndex = 0;
312 sal_Int32 nHour = aTimeString.getToken(0, ':', nIndex).toInt32();
313 sal_Int32 nMinute = aTimeString.getToken(0, ':', nIndex).toInt32();
314 sal_Int32 nSecond = aTimeString.getToken(0, ':', nIndex).toInt32();
316 Date tmpDate((sal_uInt16)nDay, (sal_uInt16)nMonth, (sal_uInt16)nYear);
317 tools::Time tmpTime(nHour, nMinute, nSecond);
318 DateTime tmpDateTime(tmpDate, tmpTime);
319 if (aString.indexOf(aUTCString) < 0)
320 tmpDateTime.ConvertToUTC();
322 aDateTime = tmpDateTime;
324 return true;
328 void xforms_daysFromDateFunction(xmlXPathParserContextPtr ctxt, int nargs)
330 // number of days from 1970-01-01 to supplied xsd:date(Time)
332 if (nargs != 1) XP_ERROR(XPATH_INVALID_ARITY);
333 xmlChar* pString = xmlXPathPopString(ctxt);
334 if (xmlXPathCheckError(ctxt)) XP_ERROR(XPATH_INVALID_TYPE);
335 OUString aString(reinterpret_cast<char*>(pString), strlen(reinterpret_cast<char*>(pString)), RTL_TEXTENCODING_UTF8);
337 DateTime aDateTime( DateTime::EMPTY );
338 if (parseDateTime(aString, aDateTime))
340 Date aReferenceDate(1, 1, 1970);
341 sal_Int32 nDays = aDateTime - aReferenceDate;
342 xmlXPathReturnNumber(ctxt, nDays);
344 else
345 xmlXPathReturnNumber(ctxt, xmlXPathNAN);
351 void xforms_secondsFromDateTimeFunction(xmlXPathParserContextPtr ctxt, int nargs)
353 // number of seconds from 1970-01-01T00:00:00Z to supplied xsd:date(Time)
355 if (nargs != 1) XP_ERROR(XPATH_INVALID_ARITY);
356 xmlChar* pString = xmlXPathPopString(ctxt);
357 if (xmlXPathCheckError(ctxt)) XP_ERROR(XPATH_INVALID_TYPE);
358 OUString aString(reinterpret_cast<char*>(pString), strlen(reinterpret_cast<char*>(pString)), RTL_TEXTENCODING_UTF8);
360 DateTime aDateTime( DateTime::EMPTY );
362 if (parseDateTime(aString, aDateTime))
364 Date aReferenceDate(1, 1, 1970);
365 sal_Int32 nDays = aDateTime - aReferenceDate;
366 sal_Int32 nSeconds = nDays * 24 * 60 * 60;
367 nSeconds += aDateTime.GetHour() * 60 * 60;
368 nSeconds += aDateTime.GetMin() * 60;
369 nSeconds += aDateTime.GetSec();
370 xmlXPathReturnNumber(ctxt, nSeconds);
372 else
373 xmlXPathReturnNumber(ctxt, xmlXPathNAN);
377 static bool parseDuration(const xmlChar* aString, bool& bNegative, sal_Int32& nYears, sal_Int32& nMonth, sal_Int32& nDays,
378 sal_Int32& nHours, sal_Int32& nMinutes, sal_Int32& nSeconds)
380 bool bTime = false; // in part after T
381 sal_Int32 nLength = strlen(reinterpret_cast<char const *>(aString))+1;
382 char *pString = static_cast<char*>(rtl_allocateMemory(nLength));
383 char *pString0 = pString;
384 strncpy(pString, reinterpret_cast<char const *>(aString), nLength);
386 if (pString[0] == '-') {
387 bNegative = true;
388 pString++;
391 if (pString[0] != 'P')
393 rtl_freeMemory(pString0);
394 return false;
397 pString++;
398 char* pToken = pString;
399 while(pToken[0] != 0)
401 switch(pToken[0]) {
402 case 'Y':
403 pToken[0] = 0;
404 nYears = atoi(pString);
405 pString = ++pToken;
406 break;
407 case 'M':
408 pToken[0] = 0;
409 if (!bTime)
410 nMonth = atoi(pString);
411 else
412 nMinutes = atoi(pString);
413 pString = ++pToken;
414 break;
415 case 'D':
416 pToken[0] = 0;
417 nDays = atoi(pString);
418 pString = ++pToken;
419 break;
420 case 'H':
421 pToken[0] = 0;
422 nHours = atoi(pString);
423 pString = ++pToken;
424 break;
425 case 'S':
426 pToken[0] = 0;
427 nSeconds = atoi(pString);
428 pString = ++pToken;
429 break;
430 case 'T':
431 bTime = true;
432 pString = ++pToken;
433 break;
434 default:
435 pToken++;
438 rtl_freeMemory(pString0);
439 return true;
442 void xforms_secondsFuction(xmlXPathParserContextPtr ctxt, int nargs)
444 // convert a xsd:duration to seconds
445 // (-)PnYnMnDTnHnMnS
446 if (nargs != 1) XP_ERROR(XPATH_INVALID_ARITY);
447 xmlChar* pString = xmlXPathPopString(ctxt);
448 if (xmlXPathCheckError(ctxt)) XP_ERROR(XPATH_INVALID_TYPE);
450 bool bNegative = false;
451 sal_Int32 nYears = 0;
452 sal_Int32 nMonths = 0;
453 sal_Int32 nDays = 0;
454 sal_Int32 nHours = 0;
455 sal_Int32 nMinutes = 0;
456 sal_Int32 nSeconds = 0;
458 if (parseDuration(pString, bNegative, nYears, nMonths, nDays, nHours, nMinutes, nSeconds))
460 nSeconds += nMinutes*60;
461 nSeconds += nHours*60*60;
462 nSeconds += nDays*24*60*60;
463 // year and month are ignored according to spec
464 if (bNegative)
465 nSeconds = 0 - nSeconds;
466 xmlXPathReturnNumber(ctxt, nSeconds);
468 else
469 xmlXPathReturnNumber(ctxt, xmlXPathNAN);
472 void xforms_monthsFuction(xmlXPathParserContextPtr ctxt, int nargs)
474 // convert a xsd:duration to seconds
475 // (-)PnYnMnDTnHnMnS
476 if (nargs != 1) XP_ERROR(XPATH_INVALID_ARITY);
477 xmlChar* pString = xmlXPathPopString(ctxt);
478 if (xmlXPathCheckError(ctxt)) XP_ERROR(XPATH_INVALID_TYPE);
480 bool bNegative = false;
481 sal_Int32 nYears = 0;
482 sal_Int32 nMonths = 0;
483 sal_Int32 nDays = 0;
484 sal_Int32 nHours = 0;
485 sal_Int32 nMinutes = 0;
486 sal_Int32 nSeconds = 0;
488 if (parseDuration(pString, bNegative, nYears, nMonths, nDays, nHours, nMinutes, nSeconds))
490 nMonths += nYears*12;
491 // Days, Houres, Minutes and seconds are ignored, see spec
492 if (bNegative)
493 nMonths = 0 - nMonths;
494 xmlXPathReturnNumber(ctxt, nMonths);
496 else
497 xmlXPathReturnNumber(ctxt, xmlXPathNAN);
501 // Node-set Functions
502 void xforms_instanceFuction(xmlXPathParserContextPtr ctxt, int nargs)
504 if (nargs != 1) XP_ERROR(XPATH_INVALID_ARITY);
505 xmlChar *pString = xmlXPathPopString(ctxt);
506 if (xmlXPathCheckError(ctxt)) XP_ERROR(XPATH_INVALID_TYPE);
507 OUString aString(reinterpret_cast<char*>(pString), strlen(reinterpret_cast<char*>(pString)), RTL_TEXTENCODING_UTF8);
509 Reference< XModel > aModel = static_cast<CLibxml2XFormsExtension*>(ctxt->context->funcLookupData)->getModel();
510 if (aModel.is())
512 Reference< XDocument > aInstance = aModel->getInstanceDocument(aString);
513 if (aInstance.is())
515 try {
516 // xmlXPathObjectPtr xmlXPathNewNodeSet (xmlNodePtr val);
517 Reference< XUnoTunnel > aTunnel(aInstance, UNO_QUERY_THROW);
518 xmlNodePtr pNode = reinterpret_cast< xmlNodePtr >( aTunnel->getSomething(Sequence< sal_Int8 >()) );
519 xmlXPathObjectPtr pObject = xmlXPathNewNodeSet(pNode);
520 xmlXPathReturnNodeSet(ctxt, pObject->nodesetval);
521 } catch (const RuntimeException&)
523 xmlXPathReturnEmptyNodeSet(ctxt);
526 else
527 xmlXPathReturnEmptyNodeSet(ctxt);
529 else
530 xmlXPathReturnEmptyNodeSet(ctxt);
534 // Node-set Functions, XForms 1.1
535 void xforms_currentFunction(xmlXPathParserContextPtr ctxt, int nargs)
537 if (nargs != 0) XP_ERROR(XPATH_INVALID_ARITY);
539 Reference< XNode > aNode = static_cast<CLibxml2XFormsExtension*>(ctxt->context->funcLookupData)->getContextNode();
541 if (aNode.is())
543 try {
544 Reference< XUnoTunnel > aTunnel(aNode, UNO_QUERY_THROW);
545 xmlNodePtr pNode = reinterpret_cast< xmlNodePtr >( aTunnel->getSomething(Sequence< sal_Int8 >()) );
546 xmlXPathObjectPtr pObject = xmlXPathNewNodeSet(pNode);
547 xmlXPathReturnNodeSet(ctxt, pObject->nodesetval);
549 catch (const RuntimeException&)
551 xmlXPathReturnEmptyNodeSet(ctxt);
554 else
555 xmlXPathReturnEmptyNodeSet(ctxt);
558 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */