update credits
[LibreOffice.git] / forms / source / xforms / xpathlib / xpathlib.cxx
blobed2c5068d8ef112fcb279bc5cb879bba6b09824e
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 = (char *)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((char*)pString, strlen((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 double nNumber = 0;
157 for (int i = 0; i < xmlXPathNodeSetGetLength(pNodeSet); i++)
159 nNumber = xmlXPathCastNodeToNumber(xmlXPathNodeSetItem(pNodeSet, i));
160 if (xmlXPathIsNaN(nNumber))
162 xmlXPathReturnNumber(ctxt, xmlXPathNAN);
163 return;
165 if (i == 0)
166 nMinimum = nNumber;
167 else if (nNumber < nMinimum)
168 nMinimum = nNumber;
170 xmlXPathReturnNumber(ctxt, nMinimum);
173 void xforms_maxFunction(xmlXPathParserContextPtr ctxt, int nargs)
175 if (nargs != 1) XP_ERROR(XPATH_INVALID_ARITY);
176 xmlNodeSetPtr pNodeSet = xmlXPathPopNodeSet(ctxt);
177 if (xmlXPathCheckError(ctxt)) XP_ERROR(XPATH_INVALID_TYPE);
178 double nMaximum = 0;
179 double nNumber = 0;
180 for (int i = 0; i < xmlXPathNodeSetGetLength(pNodeSet); i++)
182 nNumber = xmlXPathCastNodeToNumber(xmlXPathNodeSetItem(pNodeSet, i));
183 if (xmlXPathIsNaN(nNumber))
185 xmlXPathReturnNumber(ctxt, xmlXPathNAN);
186 return;
188 if (i == 0)
189 nMaximum = nNumber;
190 else if (nNumber > nMaximum)
191 nMaximum = nNumber;
193 xmlXPathReturnNumber(ctxt, nMaximum);
195 void xforms_countNonEmptyFunction(xmlXPathParserContextPtr ctxt, int nargs)
197 if (nargs != 1) XP_ERROR(XPATH_INVALID_ARITY);
198 xmlNodeSetPtr pNodeSet = xmlXPathPopNodeSet(ctxt);
199 if (xmlXPathCheckError(ctxt)) XP_ERROR(XPATH_INVALID_TYPE);
200 xmlChar *aString;
201 sal_Int32 nNotEmpty = 0;
202 for (int i = 0; i < xmlXPathNodeSetGetLength(pNodeSet); i++)
204 aString = xmlXPathCastNodeToString(xmlXPathNodeSetItem(pNodeSet, i));
205 if (*aString != 0) nNotEmpty++;
207 xmlXPathReturnNumber(ctxt, nNotEmpty);
209 void xforms_indexFunction(xmlXPathParserContextPtr /*ctxt*/, int /*nargs*/)
211 // function index takes a string argument that is the IDREF of a
212 // 'repeat' and returns the current 1-based position of the repeat
213 // index of the identified repeat -- see xforms/9.3.1
215 // doc.getElementByID
216 // (...)
219 // String Functions
220 static const char* _version = "1.0";
221 static const char* _conformance = "conformance";
222 void xforms_propertyFunction(xmlXPathParserContextPtr ctxt, int nargs)
224 if (nargs != 1) XP_ERROR(XPATH_INVALID_ARITY);
225 xmlChar* pString = xmlXPathPopString(ctxt);
226 if (xmlXPathCheckError(ctxt)) XP_ERROR(XPATH_INVALID_TYPE);
227 OUString aString((char*)pString, strlen((char*)pString), RTL_TEXTENCODING_UTF8);
228 if (aString.equalsIgnoreAsciiCase("version"))
229 xmlXPathReturnString(ctxt, (xmlChar*)_version);
230 else if (aString.equalsIgnoreAsciiCase("conformance-level"))
231 xmlXPathReturnString(ctxt, (xmlChar*)_conformance);
232 else
233 xmlXPathReturnEmptyString(ctxt);
236 // Date and Time Functions
238 static OString makeDateTimeString (const DateTime& aDateTime, sal_Bool bUTC = sal_True)
240 OStringBuffer aDateTimeString;
241 aDateTimeString.append((sal_Int32)aDateTime.GetYear());
242 aDateTimeString.append("-");
243 if (aDateTime.GetMonth()<10) aDateTimeString.append("0");
244 aDateTimeString.append((sal_Int32)aDateTime.GetMonth());
245 aDateTimeString.append("-");
246 if (aDateTime.GetDay()<10) aDateTimeString.append("0");
247 aDateTimeString.append((sal_Int32)aDateTime.GetDay());
248 aDateTimeString.append("T");
249 if (aDateTime.GetHour()<10) aDateTimeString.append("0");
250 aDateTimeString.append((sal_Int32)aDateTime.GetHour());
251 aDateTimeString.append(":");
252 if (aDateTime.GetMin()<10) aDateTimeString.append("0");
253 aDateTimeString.append((sal_Int32)aDateTime.GetMin());
254 aDateTimeString.append(":");
255 if (aDateTime.GetSec()<10) aDateTimeString.append("0");
256 aDateTimeString.append((sal_Int32)aDateTime.GetSec());
257 if (bUTC) aDateTimeString.append("Z");
259 return aDateTimeString.makeStringAndClear();
262 // returns current system date and time in canonical xsd:dateTime
263 // format
264 void xforms_nowFunction(xmlXPathParserContextPtr ctxt, int /*nargs*/)
267 A single lexical representation, which is a subset of the lexical representations
268 allowed by [ISO 8601], is allowed for dateTime. This lexical representation is the
269 [ISO 8601] extended format CCYY-MM-DDThh:mm:ss where "CC" represents the century,
270 "YY" the year, "MM" the month and "DD" the day, preceded by an optional leading "-"
271 sign to indicate a negative number. If the sign is omitted, "+" is assumed. The letter
272 "T" is the date/time separator and "hh", "mm", "ss" represent hour, minute and second
273 respectively.
277 3.2.7.2 Canonical representation
278 The canonical representation for dateTime is defined by prohibiting certain options
279 from the Lexical representation (par.3.2.7.1). Specifically, either the time zone must
280 be omitted or, if present, the time zone must be Coordinated Universal Time (UTC)
281 indicated by a "Z".
283 DateTime aDateTime( DateTime::SYSTEM );
284 OString aDateTimeString = makeDateTimeString(aDateTime);
285 xmlChar *pString = static_cast<xmlChar*>(xmlMalloc(aDateTimeString.getLength()+1));
286 strncpy((char*)pString, (char*)aDateTimeString.getStr(), aDateTimeString.getLength());
287 pString[aDateTimeString.getLength()] = 0;
288 xmlXPathReturnString(ctxt, pString);
291 static sal_Bool parseDateTime(const OUString& aString, DateTime& aDateTime)
293 // take apart a canonical literal xsd:dateTime string
294 //CCYY-MM-DDThh:mm:ss(Z)
296 OUString aDateTimeString = aString.trim();
298 // check length
299 if (aDateTimeString.getLength() < 19 || aDateTimeString.getLength() > 20)
300 return sal_False;
302 sal_Int32 nDateLength = 10;
303 sal_Int32 nTimeLength = 8;
305 OUString aUTCString("Z");
307 OUString aDateString = aDateTimeString.copy(0, nDateLength);
308 OUString aTimeString = aDateTimeString.copy(nDateLength+1, nTimeLength);
310 sal_Int32 nIndex = 0;
311 sal_Int32 nYear = aDateString.getToken(0, '-', nIndex).toInt32();
312 sal_Int32 nMonth = aDateString.getToken(0, '-', nIndex).toInt32();
313 sal_Int32 nDay = aDateString.getToken(0, '-', nIndex).toInt32();
314 nIndex = 0;
315 sal_Int32 nHour = aTimeString.getToken(0, ':', nIndex).toInt32();
316 sal_Int32 nMinute = aTimeString.getToken(0, ':', nIndex).toInt32();
317 sal_Int32 nSecond = aTimeString.getToken(0, ':', nIndex).toInt32();
319 Date tmpDate((sal_uInt16)nDay, (sal_uInt16)nMonth, (sal_uInt16)nYear);
320 Time tmpTime(nHour, nMinute, nSecond);
321 DateTime tmpDateTime(tmpDate, tmpTime);
322 if (aString.indexOf(aUTCString) < 0)
323 tmpDateTime.ConvertToUTC();
325 aDateTime = tmpDateTime;
327 return sal_True;
331 void xforms_daysFromDateFunction(xmlXPathParserContextPtr ctxt, int nargs)
333 // number of days from 1970-01-01 to supplied xsd:date(Time)
335 if (nargs != 1) XP_ERROR(XPATH_INVALID_ARITY);
336 xmlChar* pString = xmlXPathPopString(ctxt);
337 if (xmlXPathCheckError(ctxt)) XP_ERROR(XPATH_INVALID_TYPE);
338 OUString aString((char*)pString, strlen((char*)pString), RTL_TEXTENCODING_UTF8);
340 DateTime aDateTime( DateTime::EMPTY );
341 if (parseDateTime(aString, aDateTime))
343 Date aReferenceDate(1, 1, 1970);
344 sal_Int32 nDays = aDateTime - aReferenceDate;
345 xmlXPathReturnNumber(ctxt, nDays);
347 else
348 xmlXPathReturnNumber(ctxt, xmlXPathNAN);
354 void xforms_secondsFromDateTimeFunction(xmlXPathParserContextPtr ctxt, int nargs)
356 // number of seconds from 1970-01-01T00:00:00Z to supplied xsd:date(Time)
358 if (nargs != 1) XP_ERROR(XPATH_INVALID_ARITY);
359 xmlChar* pString = xmlXPathPopString(ctxt);
360 if (xmlXPathCheckError(ctxt)) XP_ERROR(XPATH_INVALID_TYPE);
361 OUString aString((char*)pString, strlen((char*)pString), RTL_TEXTENCODING_UTF8);
363 DateTime aDateTime( DateTime::EMPTY );
365 if (parseDateTime(aString, aDateTime))
367 Date aReferenceDate(1, 1, 1970);
368 sal_Int32 nDays = aDateTime - aReferenceDate;
369 sal_Int32 nSeconds = nDays * 24 * 60 * 60;
370 nSeconds += aDateTime.GetHour() * 60 * 60;
371 nSeconds += aDateTime.GetMin() * 60;
372 nSeconds += aDateTime.GetSec();
373 xmlXPathReturnNumber(ctxt, nSeconds);
375 else
376 xmlXPathReturnNumber(ctxt, xmlXPathNAN);
380 static sal_Bool parseDuration(const xmlChar* aString, sal_Bool& bNegative, sal_Int32& nYears, sal_Int32& nMonth, sal_Int32& nDays,
381 sal_Int32& nHours, sal_Int32& nMinutes, sal_Int32& nSeconds)
383 sal_Bool bTime = sal_False; // in part after T
384 sal_Int32 nLength = strlen((char*)aString)+1;
385 char *pString = (char*)rtl_allocateMemory(nLength);
386 char *pString0 = pString;
387 strncpy(pString, (char*)aString, nLength);
389 if (pString[0] == '-') {
390 bNegative = sal_True;
391 pString++;
394 if (pString[0] != 'P')
396 rtl_freeMemory(pString0);
397 return sal_False;
400 pString++;
401 char* pToken = pString;
402 while(pToken[0] != 0)
404 switch(pToken[0]) {
405 case 'Y':
406 pToken[0] = 0;
407 nYears = atoi(pString);
408 pString = ++pToken;
409 break;
410 case 'M':
411 pToken[0] = 0;
412 if (!bTime)
413 nMonth = atoi(pString);
414 else
415 nMinutes = atoi(pString);
416 pString = ++pToken;
417 break;
418 case 'D':
419 pToken[0] = 0;
420 nDays = atoi(pString);
421 pString = ++pToken;
422 break;
423 case 'H':
424 pToken[0] = 0;
425 nHours = atoi(pString);
426 pString = ++pToken;
427 break;
428 case 'S':
429 pToken[0] = 0;
430 nSeconds = atoi(pString);
431 pString = ++pToken;
432 break;
433 case 'T':
434 bTime = sal_True;
435 pString = ++pToken;
436 break;
437 default:
438 pToken++;
441 rtl_freeMemory(pString0);
442 return sal_True;
445 void xforms_secondsFuction(xmlXPathParserContextPtr ctxt, int nargs)
447 // convert a xsd:duration to seconds
448 // (-)PnYnMnDTnHnMnS
449 if (nargs != 1) XP_ERROR(XPATH_INVALID_ARITY);
450 xmlChar* pString = xmlXPathPopString(ctxt);
451 if (xmlXPathCheckError(ctxt)) XP_ERROR(XPATH_INVALID_TYPE);
453 sal_Bool bNegative = sal_False;
454 sal_Int32 nYears = 0;
455 sal_Int32 nMonths = 0;
456 sal_Int32 nDays = 0;
457 sal_Int32 nHours = 0;
458 sal_Int32 nMinutes = 0;
459 sal_Int32 nSeconds = 0;
461 if (parseDuration(pString, bNegative, nYears, nMonths, nDays, nHours, nMinutes, nSeconds))
463 nSeconds += nMinutes*60;
464 nSeconds += nHours*60*60;
465 nSeconds += nDays*24*60*60;
466 // year and month are ignored according to spec
467 if (bNegative)
468 nSeconds = 0 - nSeconds;
469 xmlXPathReturnNumber(ctxt, nSeconds);
471 else
472 xmlXPathReturnNumber(ctxt, xmlXPathNAN);
475 void xforms_monthsFuction(xmlXPathParserContextPtr ctxt, int nargs)
477 // convert a xsd:duration to seconds
478 // (-)PnYnMnDTnHnMnS
479 if (nargs != 1) XP_ERROR(XPATH_INVALID_ARITY);
480 xmlChar* pString = xmlXPathPopString(ctxt);
481 if (xmlXPathCheckError(ctxt)) XP_ERROR(XPATH_INVALID_TYPE);
483 sal_Bool bNegative = sal_False;
484 sal_Int32 nYears = 0;
485 sal_Int32 nMonths = 0;
486 sal_Int32 nDays = 0;
487 sal_Int32 nHours = 0;
488 sal_Int32 nMinutes = 0;
489 sal_Int32 nSeconds = 0;
491 if (parseDuration(pString, bNegative, nYears, nMonths, nDays, nHours, nMinutes, nSeconds))
493 nMonths += nYears*12;
494 // Days, Houres, Minutes and seconds are ignored, see spec
495 if (bNegative)
496 nMonths = 0 - nMonths;
497 xmlXPathReturnNumber(ctxt, nMonths);
499 else
500 xmlXPathReturnNumber(ctxt, xmlXPathNAN);
504 // Node-set Functions
505 void xforms_instanceFuction(xmlXPathParserContextPtr ctxt, int nargs)
507 if (nargs != 1) XP_ERROR(XPATH_INVALID_ARITY);
508 xmlChar *pString = xmlXPathPopString(ctxt);
509 if (xmlXPathCheckError(ctxt)) XP_ERROR(XPATH_INVALID_TYPE);
510 OUString aString((char*)pString, strlen((char*)pString), RTL_TEXTENCODING_UTF8);
512 Reference< XModel > aModel = ((CLibxml2XFormsExtension*)ctxt->context->funcLookupData)->getModel();
513 if (aModel.is())
515 Reference< XDocument > aInstance = aModel->getInstanceDocument(aString);
516 if (aInstance.is())
518 try {
519 // xmlXPathObjectPtr xmlXPathNewNodeSet (xmlNodePtr val);
520 Reference< XUnoTunnel > aTunnel(aInstance, UNO_QUERY_THROW);
521 xmlNodePtr pNode = reinterpret_cast< xmlNodePtr >( aTunnel->getSomething(Sequence< sal_Int8 >()) );
522 xmlXPathObjectPtr pObject = xmlXPathNewNodeSet(pNode);
523 xmlXPathReturnNodeSet(ctxt, pObject->nodesetval);
524 } catch (const RuntimeException&)
526 xmlXPathReturnEmptyNodeSet(ctxt);
529 else
530 xmlXPathReturnEmptyNodeSet(ctxt);
532 else
533 xmlXPathReturnEmptyNodeSet(ctxt);
537 // Node-set Functions, XForms 1.1
538 void xforms_currentFunction(xmlXPathParserContextPtr ctxt, int nargs)
540 if (nargs != 0) XP_ERROR(XPATH_INVALID_ARITY);
542 Reference< XNode > aNode = ((CLibxml2XFormsExtension*)ctxt->context->funcLookupData)->getContextNode();
544 if (aNode.is())
546 try {
547 Reference< XUnoTunnel > aTunnel(aNode, UNO_QUERY_THROW);
548 xmlNodePtr pNode = reinterpret_cast< xmlNodePtr >( aTunnel->getSomething(Sequence< sal_Int8 >()) );
549 xmlXPathObjectPtr pObject = xmlXPathNewNodeSet(pNode);
550 xmlXPathReturnNodeSet(ctxt, pObject->nodesetval);
552 catch (const RuntimeException&)
554 xmlXPathReturnEmptyNodeSet(ctxt);
557 else
558 xmlXPathReturnEmptyNodeSet(ctxt);
561 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */